<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<!--[if IE]><meta http-equiv="X-UA-Compatible" content="IE=edge"><![endif]-->
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="generator" content="Asciidoctor 1.5.5">
<title>重要知识点</title>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Open+Sans:300,300italic,400,400italic,600,600italic%7CNoto+Serif:400,400italic,700,700italic%7CDroid+Sans+Mono:400,700">
<style>
/* Asciidoctor default stylesheet | MIT License | http://asciidoctor.org */
/* Remove comment around @import statement below when using as a custom stylesheet */
/*@import "https://fonts.googleapis.com/css?family=Open+Sans:300,300italic,400,400italic,600,600italic%7CNoto+Serif:400,400italic,700,700italic%7CDroid+Sans+Mono:400,700";*/
article,aside,details,figcaption,figure,footer,header,hgroup,main,nav,section,summary{display:block}
audio,canvas,video{display:inline-block}
audio:not([controls]){display:none;height:0}
[hidden],template{display:none}
script{display:none!important}
html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}
a{background:transparent}
a:focus{outline:thin dotted}
a:active,a:hover{outline:0}
h1{font-size:2em;margin:.67em 0}
abbr[title]{border-bottom:1px dotted}
b,strong{font-weight:bold}
dfn{font-style:italic}
hr{-moz-box-sizing:content-box;box-sizing:content-box;height:0}
mark{background:#ff0;color:#000}
code,kbd,pre,samp{font-family:monospace;font-size:1em}
pre{white-space:pre-wrap}
q{quotes:"\201C" "\201D" "\2018" "\2019"}
small{font-size:80%}
sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}
sup{top:-.5em}
sub{bottom:-.25em}
img{border:0}
svg:not(:root){overflow:hidden}
figure{margin:0}
fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}
legend{border:0;padding:0}
button,input,select,textarea{font-family:inherit;font-size:100%;margin:0}
button,input{line-height:normal}
button,select{text-transform:none}
button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer}
button[disabled],html input[disabled]{cursor:default}
input[type="checkbox"],input[type="radio"]{box-sizing:border-box;padding:0}
input[type="search"]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}
input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}
button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}
textarea{overflow:auto;vertical-align:top}
table{border-collapse:collapse;border-spacing:0}
*,*:before,*:after{-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box}
html,body{font-size:100%}
body{background:#fff;color:rgba(0,0,0,.8);padding:0;margin:0;font-family:"Noto Serif","DejaVu Serif",serif;font-weight:400;font-style:normal;line-height:1;position:relative;cursor:auto;tab-size:4;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased}
a:hover{cursor:pointer}
img,object,embed{max-width:100%;height:auto}
object,embed{height:100%}
img{-ms-interpolation-mode:bicubic}
.left{float:left!important}
.right{float:right!important}
.text-left{text-align:left!important}
.text-right{text-align:right!important}
.text-center{text-align:center!important}
.text-justify{text-align:justify!important}
.hide{display:none}
img,object,svg{display:inline-block;vertical-align:middle}
textarea{height:auto;min-height:50px}
select{width:100%}
.center{margin-left:auto;margin-right:auto}
.spread{width:100%}
p.lead,.paragraph.lead>p,#preamble>.sectionbody>.paragraph:first-of-type p{font-size:1.21875em;line-height:1.6}
.subheader,.admonitionblock td.content>.title,.audioblock>.title,.exampleblock>.title,.imageblock>.title,.listingblock>.title,.literalblock>.title,.stemblock>.title,.openblock>.title,.paragraph>.title,.quoteblock>.title,table.tableblock>.title,.verseblock>.title,.videoblock>.title,.dlist>.title,.olist>.title,.ulist>.title,.qlist>.title,.hdlist>.title{line-height:1.45;color:#7a2518;font-weight:400;margin-top:0;margin-bottom:.25em}
div,dl,dt,dd,ul,ol,li,h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6,pre,form,p,blockquote,th,td{margin:0;padding:0;direction:ltr}
a{color:#2156a5;text-decoration:underline;line-height:inherit}
a:hover,a:focus{color:#1d4b8f}
a img{border:none}
p{font-family:inherit;font-weight:400;font-size:1em;line-height:1.6;margin-bottom:1.25em;text-rendering:optimizeLegibility}
p aside{font-size:.875em;line-height:1.35;font-style:italic}
h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{font-family:"Open Sans","DejaVu Sans",sans-serif;font-weight:300;font-style:normal;color:#ba3925;text-rendering:optimizeLegibility;margin-top:1em;margin-bottom:.5em;line-height:1.0125em}
h1 small,h2 small,h3 small,#toctitle small,.sidebarblock>.content>.title small,h4 small,h5 small,h6 small{font-size:60%;color:#e99b8f;line-height:0}
h1{font-size:2.125em}
h2{font-size:1.6875em}
h3,#toctitle,.sidebarblock>.content>.title{font-size:1.375em}
h4,h5{font-size:1.125em}
h6{font-size:1em}
hr{border:solid #ddddd8;border-width:1px 0 0;clear:both;margin:1.25em 0 1.1875em;height:0}
em,i{font-style:italic;line-height:inherit}
strong,b{font-weight:bold;line-height:inherit}
small{font-size:60%;line-height:inherit}
code{font-family:"Droid Sans Mono","DejaVu Sans Mono",monospace;font-weight:400;color:rgba(0,0,0,.9)}
ul,ol,dl{font-size:1em;line-height:1.6;margin-bottom:1.25em;list-style-position:outside;font-family:inherit}
ul,ol,ul.no-bullet,ol.no-bullet{margin-left:1.5em}
ul li ul,ul li ol{margin-left:1.25em;margin-bottom:0;font-size:1em}
ul.square li ul,ul.circle li ul,ul.disc li ul{list-style:inherit}
ul.square{list-style-type:square}
ul.circle{list-style-type:circle}
ul.disc{list-style-type:disc}
ul.no-bullet{list-style:none}
ol li ul,ol li ol{margin-left:1.25em;margin-bottom:0}
dl dt{margin-bottom:.3125em;font-weight:bold}
dl dd{margin-bottom:1.25em}
abbr,acronym{text-transform:uppercase;font-size:90%;color:rgba(0,0,0,.8);border-bottom:1px dotted #ddd;cursor:help}
abbr{text-transform:none}
blockquote{margin:0 0 1.25em;padding:.5625em 1.25em 0 1.1875em;border-left:1px solid #ddd}
blockquote cite{display:block;font-size:.9375em;color:rgba(0,0,0,.6)}
blockquote cite:before{content:"\2014 \0020"}
blockquote cite a,blockquote cite a:visited{color:rgba(0,0,0,.6)}
blockquote,blockquote p{line-height:1.6;color:rgba(0,0,0,.85)}
@media only screen and (min-width:768px){h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{line-height:1.2}
h1{font-size:2.75em}
h2{font-size:2.3125em}
h3,#toctitle,.sidebarblock>.content>.title{font-size:1.6875em}
h4{font-size:1.4375em}}
table{background:#fff;margin-bottom:1.25em;border:solid 1px #dedede}
table thead,table tfoot{background:#f7f8f7;font-weight:bold}
table thead tr th,table thead tr td,table tfoot tr th,table tfoot tr td{padding:.5em .625em .625em;font-size:inherit;color:rgba(0,0,0,.8);text-align:left}
table tr th,table tr td{padding:.5625em .625em;font-size:inherit;color:rgba(0,0,0,.8)}
table tr.even,table tr.alt,table tr:nth-of-type(even){background:#f8f8f7}
table thead tr th,table tfoot tr th,table tbody tr td,table tr td,table tfoot tr td{display:table-cell;line-height:1.6}
h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{line-height:1.2;word-spacing:-.05em}
h1 strong,h2 strong,h3 strong,#toctitle strong,.sidebarblock>.content>.title strong,h4 strong,h5 strong,h6 strong{font-weight:400}
.clearfix:before,.clearfix:after,.float-group:before,.float-group:after{content:" ";display:table}
.clearfix:after,.float-group:after{clear:both}
*:not(pre)>code{font-size:.9375em;font-style:normal!important;letter-spacing:0;padding:.1em .5ex;word-spacing:-.15em;background-color:#f7f7f8;-webkit-border-radius:4px;border-radius:4px;line-height:1.45;text-rendering:optimizeSpeed;word-wrap:break-word}
*:not(pre)>code.nobreak{word-wrap:normal}
*:not(pre)>code.nowrap{white-space:nowrap}
pre,pre>code{line-height:1.45;color:rgba(0,0,0,.9);font-family:"Droid Sans Mono","DejaVu Sans Mono",monospace;font-weight:400;text-rendering:optimizeSpeed}
em em{font-style:normal}
strong strong{font-weight:400}
.keyseq{color:rgba(51,51,51,.8)}
kbd{font-family:"Droid Sans Mono","DejaVu Sans Mono",monospace;display:inline-block;color:rgba(0,0,0,.8);font-size:.65em;line-height:1.45;background-color:#f7f7f7;border:1px solid #ccc;-webkit-border-radius:3px;border-radius:3px;-webkit-box-shadow:0 1px 0 rgba(0,0,0,.2),0 0 0 .1em white inset;box-shadow:0 1px 0 rgba(0,0,0,.2),0 0 0 .1em #fff inset;margin:0 .15em;padding:.2em .5em;vertical-align:middle;position:relative;top:-.1em;white-space:nowrap}
.keyseq kbd:first-child{margin-left:0}
.keyseq kbd:last-child{margin-right:0}
.menuseq,.menu{color:rgba(0,0,0,.8)}
b.button:before,b.button:after{position:relative;top:-1px;font-weight:400}
b.button:before{content:"[";padding:0 3px 0 2px}
b.button:after{content:"]";padding:0 2px 0 3px}
p a>code:hover{color:rgba(0,0,0,.9)}
#header,#content,#footnotes,#footer{width:100%;margin-left:auto;margin-right:auto;margin-top:0;margin-bottom:0;max-width:62.5em;*zoom:1;position:relative;padding-left:.9375em;padding-right:.9375em}
#header:before,#header:after,#content:before,#content:after,#footnotes:before,#footnotes:after,#footer:before,#footer:after{content:" ";display:table}
#header:after,#content:after,#footnotes:after,#footer:after{clear:both}
#content{margin-top:1.25em}
#content:before{content:none}
#header>h1:first-child{color:rgba(0,0,0,.85);margin-top:2.25rem;margin-bottom:0}
#header>h1:first-child+#toc{margin-top:8px;border-top:1px solid #ddddd8}
#header>h1:only-child,body.toc2 #header>h1:nth-last-child(2){border-bottom:1px solid #ddddd8;padding-bottom:8px}
#header .details{border-bottom:1px solid #ddddd8;line-height:1.45;padding-top:.25em;padding-bottom:.25em;padding-left:.25em;color:rgba(0,0,0,.6);display:-ms-flexbox;display:-webkit-flex;display:flex;-ms-flex-flow:row wrap;-webkit-flex-flow:row wrap;flex-flow:row wrap}
#header .details span:first-child{margin-left:-.125em}
#header .details span.email a{color:rgba(0,0,0,.85)}
#header .details br{display:none}
#header .details br+span:before{content:"\00a0\2013\00a0"}
#header .details br+span.author:before{content:"\00a0\22c5\00a0";color:rgba(0,0,0,.85)}
#header .details br+span#revremark:before{content:"\00a0|\00a0"}
#header #revnumber{text-transform:capitalize}
#header #revnumber:after{content:"\00a0"}
#content>h1:first-child:not([class]){color:rgba(0,0,0,.85);border-bottom:1px solid #ddddd8;padding-bottom:8px;margin-top:0;padding-top:1rem;margin-bottom:1.25rem}
#toc{border-bottom:1px solid #efefed;padding-bottom:.5em}
#toc>ul{margin-left:.125em}
#toc ul.sectlevel0>li>a{font-style:italic}
#toc ul.sectlevel0 ul.sectlevel1{margin:.5em 0}
#toc ul{font-family:"Open Sans","DejaVu Sans",sans-serif;list-style-type:none}
#toc li{line-height:1.3334;margin-top:.3334em}
#toc a{text-decoration:none}
#toc a:active{text-decoration:underline}
#toctitle{color:#7a2518;font-size:1.2em}
@media only screen and (min-width:768px){#toctitle{font-size:1.375em}
body.toc2{padding-left:15em;padding-right:0}
#toc.toc2{margin-top:0!important;background-color:#f8f8f7;position:fixed;width:15em;left:0;top:0;border-right:1px solid #efefed;border-top-width:0!important;border-bottom-width:0!important;z-index:1000;padding:1.25em 1em;height:100%;overflow:auto}
#toc.toc2 #toctitle{margin-top:0;margin-bottom:.8rem;font-size:1.2em}
#toc.toc2>ul{font-size:.9em;margin-bottom:0}
#toc.toc2 ul ul{margin-left:0;padding-left:1em}
#toc.toc2 ul.sectlevel0 ul.sectlevel1{padding-left:0;margin-top:.5em;margin-bottom:.5em}
body.toc2.toc-right{padding-left:0;padding-right:15em}
body.toc2.toc-right #toc.toc2{border-right-width:0;border-left:1px solid #efefed;left:auto;right:0}}
@media only screen and (min-width:1280px){body.toc2{padding-left:20em;padding-right:0}
#toc.toc2{width:20em}
#toc.toc2 #toctitle{font-size:1.375em}
#toc.toc2>ul{font-size:.95em}
#toc.toc2 ul ul{padding-left:1.25em}
body.toc2.toc-right{padding-left:0;padding-right:20em}}
#content #toc{border-style:solid;border-width:1px;border-color:#e0e0dc;margin-bottom:1.25em;padding:1.25em;background:#f8f8f7;-webkit-border-radius:4px;border-radius:4px}
#content #toc>:first-child{margin-top:0}
#content #toc>:last-child{margin-bottom:0}
#footer{max-width:100%;background-color:rgba(0,0,0,.8);padding:1.25em}
#footer-text{color:rgba(255,255,255,.8);line-height:1.44}
.sect1{padding-bottom:.625em}
@media only screen and (min-width:768px){.sect1{padding-bottom:1.25em}}
.sect1+.sect1{border-top:1px solid #efefed}
#content h1>a.anchor,h2>a.anchor,h3>a.anchor,#toctitle>a.anchor,.sidebarblock>.content>.title>a.anchor,h4>a.anchor,h5>a.anchor,h6>a.anchor{position:absolute;z-index:1001;width:1.5ex;margin-left:-1.5ex;display:block;text-decoration:none!important;visibility:hidden;text-align:center;font-weight:400}
#content h1>a.anchor:before,h2>a.anchor:before,h3>a.anchor:before,#toctitle>a.anchor:before,.sidebarblock>.content>.title>a.anchor:before,h4>a.anchor:before,h5>a.anchor:before,h6>a.anchor:before{content:"\00A7";font-size:.85em;display:block;padding-top:.1em}
#content h1:hover>a.anchor,#content h1>a.anchor:hover,h2:hover>a.anchor,h2>a.anchor:hover,h3:hover>a.anchor,#toctitle:hover>a.anchor,.sidebarblock>.content>.title:hover>a.anchor,h3>a.anchor:hover,#toctitle>a.anchor:hover,.sidebarblock>.content>.title>a.anchor:hover,h4:hover>a.anchor,h4>a.anchor:hover,h5:hover>a.anchor,h5>a.anchor:hover,h6:hover>a.anchor,h6>a.anchor:hover{visibility:visible}
#content h1>a.link,h2>a.link,h3>a.link,#toctitle>a.link,.sidebarblock>.content>.title>a.link,h4>a.link,h5>a.link,h6>a.link{color:#ba3925;text-decoration:none}
#content h1>a.link:hover,h2>a.link:hover,h3>a.link:hover,#toctitle>a.link:hover,.sidebarblock>.content>.title>a.link:hover,h4>a.link:hover,h5>a.link:hover,h6>a.link:hover{color:#a53221}
.audioblock,.imageblock,.literalblock,.listingblock,.stemblock,.videoblock{margin-bottom:1.25em}
.admonitionblock td.content>.title,.audioblock>.title,.exampleblock>.title,.imageblock>.title,.listingblock>.title,.literalblock>.title,.stemblock>.title,.openblock>.title,.paragraph>.title,.quoteblock>.title,table.tableblock>.title,.verseblock>.title,.videoblock>.title,.dlist>.title,.olist>.title,.ulist>.title,.qlist>.title,.hdlist>.title{text-rendering:optimizeLegibility;text-align:left;font-family:"Noto Serif","DejaVu Serif",serif;font-size:1rem;font-style:italic}
table.tableblock>caption.title{white-space:nowrap;overflow:visible;max-width:0}
.paragraph.lead>p,#preamble>.sectionbody>.paragraph:first-of-type p{color:rgba(0,0,0,.85)}
table.tableblock #preamble>.sectionbody>.paragraph:first-of-type p{font-size:inherit}
.admonitionblock>table{border-collapse:separate;border:0;background:none;width:100%}
.admonitionblock>table td.icon{text-align:center;width:80px}
.admonitionblock>table td.icon img{max-width:none}
.admonitionblock>table td.icon .title{font-weight:bold;font-family:"Open Sans","DejaVu Sans",sans-serif;text-transform:uppercase}
.admonitionblock>table td.content{padding-left:1.125em;padding-right:1.25em;border-left:1px solid #ddddd8;color:rgba(0,0,0,.6)}
.admonitionblock>table td.content>:last-child>:last-child{margin-bottom:0}
.exampleblock>.content{border-style:solid;border-width:1px;border-color:#e6e6e6;margin-bottom:1.25em;padding:1.25em;background:#fff;-webkit-border-radius:4px;border-radius:4px}
.exampleblock>.content>:first-child{margin-top:0}
.exampleblock>.content>:last-child{margin-bottom:0}
.sidebarblock{border-style:solid;border-width:1px;border-color:#e0e0dc;margin-bottom:1.25em;padding:1.25em;background:#f8f8f7;-webkit-border-radius:4px;border-radius:4px}
.sidebarblock>:first-child{margin-top:0}
.sidebarblock>:last-child{margin-bottom:0}
.sidebarblock>.content>.title{color:#7a2518;margin-top:0;text-align:center}
.exampleblock>.content>:last-child>:last-child,.exampleblock>.content .olist>ol>li:last-child>:last-child,.exampleblock>.content .ulist>ul>li:last-child>:last-child,.exampleblock>.content .qlist>ol>li:last-child>:last-child,.sidebarblock>.content>:last-child>:last-child,.sidebarblock>.content .olist>ol>li:last-child>:last-child,.sidebarblock>.content .ulist>ul>li:last-child>:last-child,.sidebarblock>.content .qlist>ol>li:last-child>:last-child{margin-bottom:0}
.literalblock pre,.listingblock pre:not(.highlight),.listingblock pre[class="highlight"],.listingblock pre[class^="highlight "],.listingblock pre.CodeRay,.listingblock pre.prettyprint{background:#f7f7f8}
.sidebarblock .literalblock pre,.sidebarblock .listingblock pre:not(.highlight),.sidebarblock .listingblock pre[class="highlight"],.sidebarblock .listingblock pre[class^="highlight "],.sidebarblock .listingblock pre.CodeRay,.sidebarblock .listingblock pre.prettyprint{background:#f2f1f1}
.literalblock pre,.literalblock pre[class],.listingblock pre,.listingblock pre[class]{-webkit-border-radius:4px;border-radius:4px;word-wrap:break-word;padding:1em;font-size:.8125em}
.literalblock pre.nowrap,.literalblock pre[class].nowrap,.listingblock pre.nowrap,.listingblock pre[class].nowrap{overflow-x:auto;white-space:pre;word-wrap:normal}
@media only screen and (min-width:768px){.literalblock pre,.literalblock pre[class],.listingblock pre,.listingblock pre[class]{font-size:.90625em}}
@media only screen and (min-width:1280px){.literalblock pre,.literalblock pre[class],.listingblock pre,.listingblock pre[class]{font-size:1em}}
.literalblock.output pre{color:#f7f7f8;background-color:rgba(0,0,0,.9)}
.listingblock pre.highlightjs{padding:0}
.listingblock pre.highlightjs>code{padding:1em;-webkit-border-radius:4px;border-radius:4px}
.listingblock pre.prettyprint{border-width:0}
.listingblock>.content{position:relative}
.listingblock code[data-lang]:before{display:none;content:attr(data-lang);position:absolute;font-size:.75em;top:.425rem;right:.5rem;line-height:1;text-transform:uppercase;color:#999}
.listingblock:hover code[data-lang]:before{display:block}
.listingblock.terminal pre .command:before{content:attr(data-prompt);padding-right:.5em;color:#999}
.listingblock.terminal pre .command:not([data-prompt]):before{content:"$"}
table.pyhltable{border-collapse:separate;border:0;margin-bottom:0;background:none}
table.pyhltable td{vertical-align:top;padding-top:0;padding-bottom:0;line-height:1.45}
table.pyhltable td.code{padding-left:.75em;padding-right:0}
pre.pygments .lineno,table.pyhltable td:not(.code){color:#999;padding-left:0;padding-right:.5em;border-right:1px solid #ddddd8}
pre.pygments .lineno{display:inline-block;margin-right:.25em}
table.pyhltable .linenodiv{background:none!important;padding-right:0!important}
.quoteblock{margin:0 1em 1.25em 1.5em;display:table}
.quoteblock>.title{margin-left:-1.5em;margin-bottom:.75em}
.quoteblock blockquote,.quoteblock blockquote p{color:rgba(0,0,0,.85);font-size:1.15rem;line-height:1.75;word-spacing:.1em;letter-spacing:0;font-style:italic;text-align:justify}
.quoteblock blockquote{margin:0;padding:0;border:0}
.quoteblock blockquote:before{content:"\201c";float:left;font-size:2.75em;font-weight:bold;line-height:.6em;margin-left:-.6em;color:#7a2518;text-shadow:0 1px 2px rgba(0,0,0,.1)}
.quoteblock blockquote>.paragraph:last-child p{margin-bottom:0}
.quoteblock .attribution{margin-top:.5em;margin-right:.5ex;text-align:right}
.quoteblock .quoteblock{margin-left:0;margin-right:0;padding:.5em 0;border-left:3px solid rgba(0,0,0,.6)}
.quoteblock .quoteblock blockquote{padding:0 0 0 .75em}
.quoteblock .quoteblock blockquote:before{display:none}
.verseblock{margin:0 1em 1.25em 1em}
.verseblock pre{font-family:"Open Sans","DejaVu Sans",sans;font-size:1.15rem;color:rgba(0,0,0,.85);font-weight:300;text-rendering:optimizeLegibility}
.verseblock pre strong{font-weight:400}
.verseblock .attribution{margin-top:1.25rem;margin-left:.5ex}
.quoteblock .attribution,.verseblock .attribution{font-size:.9375em;line-height:1.45;font-style:italic}
.quoteblock .attribution br,.verseblock .attribution br{display:none}
.quoteblock .attribution cite,.verseblock .attribution cite{display:block;letter-spacing:-.025em;color:rgba(0,0,0,.6)}
.quoteblock.abstract{margin:0 0 1.25em 0;display:block}
.quoteblock.abstract blockquote,.quoteblock.abstract blockquote p{text-align:left;word-spacing:0}
.quoteblock.abstract blockquote:before,.quoteblock.abstract blockquote p:first-of-type:before{display:none}
table.tableblock{max-width:100%;border-collapse:separate}
table.tableblock td>.paragraph:last-child p>p:last-child,table.tableblock th>p:last-child,table.tableblock td>p:last-child{margin-bottom:0}
table.tableblock,th.tableblock,td.tableblock{border:0 solid #dedede}
table.grid-all th.tableblock,table.grid-all td.tableblock{border-width:0 1px 1px 0}
table.grid-all tfoot>tr>th.tableblock,table.grid-all tfoot>tr>td.tableblock{border-width:1px 1px 0 0}
table.grid-cols th.tableblock,table.grid-cols td.tableblock{border-width:0 1px 0 0}
table.grid-all *>tr>.tableblock:last-child,table.grid-cols *>tr>.tableblock:last-child{border-right-width:0}
table.grid-rows th.tableblock,table.grid-rows td.tableblock{border-width:0 0 1px 0}
table.grid-all tbody>tr:last-child>th.tableblock,table.grid-all tbody>tr:last-child>td.tableblock,table.grid-all thead:last-child>tr>th.tableblock,table.grid-rows tbody>tr:last-child>th.tableblock,table.grid-rows tbody>tr:last-child>td.tableblock,table.grid-rows thead:last-child>tr>th.tableblock{border-bottom-width:0}
table.grid-rows tfoot>tr>th.tableblock,table.grid-rows tfoot>tr>td.tableblock{border-width:1px 0 0 0}
table.frame-all{border-width:1px}
table.frame-sides{border-width:0 1px}
table.frame-topbot{border-width:1px 0}
th.halign-left,td.halign-left{text-align:left}
th.halign-right,td.halign-right{text-align:right}
th.halign-center,td.halign-center{text-align:center}
th.valign-top,td.valign-top{vertical-align:top}
th.valign-bottom,td.valign-bottom{vertical-align:bottom}
th.valign-middle,td.valign-middle{vertical-align:middle}
table thead th,table tfoot th{font-weight:bold}
tbody tr th{display:table-cell;line-height:1.6;background:#f7f8f7}
tbody tr th,tbody tr th p,tfoot tr th,tfoot tr th p{color:rgba(0,0,0,.8);font-weight:bold}
p.tableblock>code:only-child{background:none;padding:0}
p.tableblock{font-size:1em}
td>div.verse{white-space:pre}
ol{margin-left:1.75em}
ul li ol{margin-left:1.5em}
dl dd{margin-left:1.125em}
dl dd:last-child,dl dd:last-child>:last-child{margin-bottom:0}
ol>li p,ul>li p,ul dd,ol dd,.olist .olist,.ulist .ulist,.ulist .olist,.olist .ulist{margin-bottom:.625em}
ul.unstyled,ol.unnumbered,ul.checklist,ul.none{list-style-type:none}
ul.unstyled,ol.unnumbered,ul.checklist{margin-left:.625em}
ul.checklist li>p:first-child>.fa-square-o:first-child,ul.checklist li>p:first-child>.fa-check-square-o:first-child{width:1em;font-size:.85em}
ul.checklist li>p:first-child>input[type="checkbox"]:first-child{width:1em;position:relative;top:1px}
ul.inline{margin:0 auto .625em auto;margin-left:-1.375em;margin-right:0;padding:0;list-style:none;overflow:hidden}
ul.inline>li{list-style:none;float:left;margin-left:1.375em;display:block}
ul.inline>li>*{display:block}
.unstyled dl dt{font-weight:400;font-style:normal}
ol.arabic{list-style-type:decimal}
ol.decimal{list-style-type:decimal-leading-zero}
ol.loweralpha{list-style-type:lower-alpha}
ol.upperalpha{list-style-type:upper-alpha}
ol.lowerroman{list-style-type:lower-roman}
ol.upperroman{list-style-type:upper-roman}
ol.lowergreek{list-style-type:lower-greek}
.hdlist>table,.colist>table{border:0;background:none}
.hdlist>table>tbody>tr,.colist>table>tbody>tr{background:none}
td.hdlist1,td.hdlist2{vertical-align:top;padding:0 .625em}
td.hdlist1{font-weight:bold;padding-bottom:1.25em}
.literalblock+.colist,.listingblock+.colist{margin-top:-.5em}
.colist>table tr>td:first-of-type{padding:0 .75em;line-height:1}
.colist>table tr>td:last-of-type{padding:.25em 0}
.thumb,.th{line-height:0;display:inline-block;border:solid 4px #fff;-webkit-box-shadow:0 0 0 1px #ddd;box-shadow:0 0 0 1px #ddd}
.imageblock.left,.imageblock[style*="float: left"]{margin:.25em .625em 1.25em 0}
.imageblock.right,.imageblock[style*="float: right"]{margin:.25em 0 1.25em .625em}
.imageblock>.title{margin-bottom:0}
.imageblock.thumb,.imageblock.th{border-width:6px}
.imageblock.thumb>.title,.imageblock.th>.title{padding:0 .125em}
.image.left,.image.right{margin-top:.25em;margin-bottom:.25em;display:inline-block;line-height:0}
.image.left{margin-right:.625em}
.image.right{margin-left:.625em}
a.image{text-decoration:none;display:inline-block}
a.image object{pointer-events:none}
sup.footnote,sup.footnoteref{font-size:.875em;position:static;vertical-align:super}
sup.footnote a,sup.footnoteref a{text-decoration:none}
sup.footnote a:active,sup.footnoteref a:active{text-decoration:underline}
#footnotes{padding-top:.75em;padding-bottom:.75em;margin-bottom:.625em}
#footnotes hr{width:20%;min-width:6.25em;margin:-.25em 0 .75em 0;border-width:1px 0 0 0}
#footnotes .footnote{padding:0 .375em 0 .225em;line-height:1.3334;font-size:.875em;margin-left:1.2em;text-indent:-1.05em;margin-bottom:.2em}
#footnotes .footnote a:first-of-type{font-weight:bold;text-decoration:none}
#footnotes .footnote:last-of-type{margin-bottom:0}
#content #footnotes{margin-top:-.625em;margin-bottom:0;padding:.75em 0}
.gist .file-data>table{border:0;background:#fff;width:100%;margin-bottom:0}
.gist .file-data>table td.line-data{width:99%}
div.unbreakable{page-break-inside:avoid}
.big{font-size:larger}
.small{font-size:smaller}
.underline{text-decoration:underline}
.overline{text-decoration:overline}
.line-through{text-decoration:line-through}
.aqua{color:#00bfbf}
.aqua-background{background-color:#00fafa}
.black{color:#000}
.black-background{background-color:#000}
.blue{color:#0000bf}
.blue-background{background-color:#0000fa}
.fuchsia{color:#bf00bf}
.fuchsia-background{background-color:#fa00fa}
.gray{color:#606060}
.gray-background{background-color:#7d7d7d}
.green{color:#006000}
.green-background{background-color:#007d00}
.lime{color:#00bf00}
.lime-background{background-color:#00fa00}
.maroon{color:#600000}
.maroon-background{background-color:#7d0000}
.navy{color:#000060}
.navy-background{background-color:#00007d}
.olive{color:#606000}
.olive-background{background-color:#7d7d00}
.purple{color:#600060}
.purple-background{background-color:#7d007d}
.red{color:#bf0000}
.red-background{background-color:#fa0000}
.silver{color:#909090}
.silver-background{background-color:#bcbcbc}
.teal{color:#006060}
.teal-background{background-color:#007d7d}
.white{color:#bfbfbf}
.white-background{background-color:#fafafa}
.yellow{color:#bfbf00}
.yellow-background{background-color:#fafa00}
span.icon>.fa{cursor:default}
.admonitionblock td.icon [class^="fa icon-"]{font-size:2.5em;text-shadow:1px 1px 2px rgba(0,0,0,.5);cursor:default}
.admonitionblock td.icon .icon-note:before{content:"\f05a";color:#19407c}
.admonitionblock td.icon .icon-tip:before{content:"\f0eb";text-shadow:1px 1px 2px rgba(155,155,0,.8);color:#111}
.admonitionblock td.icon .icon-warning:before{content:"\f071";color:#bf6900}
.admonitionblock td.icon .icon-caution:before{content:"\f06d";color:#bf3400}
.admonitionblock td.icon .icon-important:before{content:"\f06a";color:#bf0000}
.conum[data-value]{display:inline-block;color:#fff!important;background-color:rgba(0,0,0,.8);-webkit-border-radius:100px;border-radius:100px;text-align:center;font-size:.75em;width:1.67em;height:1.67em;line-height:1.67em;font-family:"Open Sans","DejaVu Sans",sans-serif;font-style:normal;font-weight:bold}
.conum[data-value] *{color:#fff!important}
.conum[data-value]+b{display:none}
.conum[data-value]:after{content:attr(data-value)}
pre .conum[data-value]{position:relative;top:-.125em}
b.conum *{color:inherit!important}
.conum:not([data-value]):empty{display:none}
dt,th.tableblock,td.content,div.footnote{text-rendering:optimizeLegibility}
h1,h2,p,td.content,span.alt{letter-spacing:-.01em}
p strong,td.content strong,div.footnote strong{letter-spacing:-.005em}
p,blockquote,dt,td.content,span.alt{font-size:1.0625rem}
p{margin-bottom:1.25rem}
.sidebarblock p,.sidebarblock dt,.sidebarblock td.content,p.tableblock{font-size:1em}
.exampleblock>.content{background-color:#fffef7;border-color:#e0e0dc;-webkit-box-shadow:0 1px 4px #e0e0dc;box-shadow:0 1px 4px #e0e0dc}
.print-only{display:none!important}
@media print{@page{margin:1.25cm .75cm}
*{-webkit-box-shadow:none!important;box-shadow:none!important;text-shadow:none!important}
a{color:inherit!important;text-decoration:underline!important}
a.bare,a[href^="#"],a[href^="mailto:"]{text-decoration:none!important}
a[href^="http:"]:not(.bare):after,a[href^="https:"]:not(.bare):after{content:"(" attr(href) ")";display:inline-block;font-size:.875em;padding-left:.25em}
abbr[title]:after{content:" (" attr(title) ")"}
pre,blockquote,tr,img,object,svg{page-break-inside:avoid}
thead{display:table-header-group}
svg{max-width:100%}
p,blockquote,dt,td.content{font-size:1em;orphans:3;widows:3}
h2,h3,#toctitle,.sidebarblock>.content>.title{page-break-after:avoid}
#toc,.sidebarblock,.exampleblock>.content{background:none!important}
#toc{border-bottom:1px solid #ddddd8!important;padding-bottom:0!important}
.sect1{padding-bottom:0!important}
.sect1+.sect1{border:0!important}
#header>h1:first-child{margin-top:1.25rem}
body.book #header{text-align:center}
body.book #header>h1:first-child{border:0!important;margin:2.5em 0 1em 0}
body.book #header .details{border:0!important;display:block;padding:0!important}
body.book #header .details span:first-child{margin-left:0!important}
body.book #header .details br{display:block}
body.book #header .details br+span:before{content:none!important}
body.book #toc{border:0!important;text-align:left!important;padding:0!important;margin:0!important}
body.book #toc,body.book #preamble,body.book h1.sect0,body.book .sect1>h2{page-break-before:always}
.listingblock code[data-lang]:before{display:block}
#footer{background:none!important;padding:0 .9375em}
#footer-text{color:rgba(0,0,0,.6)!important;font-size:.9em}
.hide-on-print{display:none!important}
.print-only{display:block!important}
.hide-for-print{display:none!important}
.show-for-print{display:inherit!important}}
</style>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.6.3/css/font-awesome.min.css">
</head>
<body class="book">
<div id="header">
<h1>重要知识点</h1>
<div id="toc" class="toc">
<div id="toctitle">重要知识点</div>
<ul class="sectlevel0">
<li><a href="#_分布式">分布式</a>
<ul class="sectlevel1">
<li><a href="#_分布式幂等性如何设计">1. 分布式幂等性如何设计？</a></li>
<li><a href="#_说说你对分布式事务的了解">2. 说说你对分布式事务的了解？</a></li>
<li><a href="#_什么是_2_阶段提交协议_2pc">3. 什么是 2 阶段提交协议？ 2pc</a></li>
<li><a href="#_什么是补偿事务">4. 什么是补偿事务？</a></li>
<li><a href="#_如何用消息队列实现分布式事务的">5. 如何用消息队列实现分布式事务的？</a></li>
<li><a href="#_分布式_id_生成有几种方案">6. 分布式 ID 生成有几种方案？</a></li>
<li><a href="#_常见的负载均衡算法有那些">7. 常见的负载均衡算法有那些？</a></li>
<li><a href="#_什么是计数器_固定窗口_算法">8. 什么是计数器（固定窗口）算法？</a></li>
<li><a href="#_什么是滑动窗口算法">9. 什么是滑动窗口算法？</a></li>
<li><a href="#_什么是漏桶算法">10. 什么是漏桶算法？</a></li>
<li><a href="#_什么是令牌桶算法">11. 什么是令牌桶算法？</a></li>
<li><a href="#_数据库如何处理大数据量">12. 数据库如何处理大数据量？</a></li>
<li><a href="#_什么是cap理论">13. 什么是CAP理论？</a></li>
<li><a href="#_什么是_base_理论">14. 什么是 BASE 理论？</a></li>
<li><a href="#_什么是可靠消息最终一致性方案">15. 什么是可靠消息最终一致性方案？</a></li>
</ul>
</li>
<li><a href="#_消息中间件">消息中间件</a>
<ul class="sectlevel1">
<li><a href="#_为什么使用消息队列_mq">16. 为什么使用消息队列 MQ ?</a></li>
<li><a href="#_如何选择消息队列">17. 如何选择消息队列</a></li>
<li><a href="#_如何保证消息不丢失">18. 如何保证消息不丢失？</a></li>
<li><a href="#_什么是消息重复">19. 什么是消息重复？</a></li>
<li><a href="#_如何解决mq中的重复消息">20. 如何解决MQ中的重复消息？</a></li>
<li><a href="#_消息队列的优缺点_使用场景">21. 消息队列的优缺点、使用场景</a></li>
<li><a href="#_如何保证消息可靠传输">22. 如何保证消息可靠传输</a></li>
<li><a href="#_死信队列_延时队列">23. 死信队列、延时队列</a></li>
</ul>
</li>
<li><a href="#_java_基础相关">Java 基础相关</a>
<ul class="sectlevel1">
<li><a href="#_java中线程的实现方式">24. Java中线程的实现方式</a></li>
<li><a href="#_java中线程的状态">25. Java中线程的状态</a></li>
<li><a href="#_如何停止线程">26. 如何停止线程</a></li>
<li><a href="#_java中_sleep_和_wait_方法区别">27. Java中 sleep 和 wait 方法区别</a></li>
<li><a href="#_java_并发编程的三大特性">28. Java 并发编程的三大特性</a></li>
<li><a href="#_什么是cas_有什么特性">29. 什么是CAS，有什么特性？</a></li>
<li><a href="#__content_注解的作用">30. @Content 注解的作用</a></li>
<li><a href="#_java_中的四种引用类型">31. Java 中的四种引用类型</a></li>
<li><a href="#_threadlocal_是什么以及内存泄漏问题">32. ThreadLocal 是什么以及内存泄漏问题</a></li>
<li><a href="#_java_中锁的分类">33. Java 中锁的分类</a></li>
<li><a href="#_synchronized_在1_6的优化">34. synchronized 在1.6的优化</a></li>
<li><a href="#_synchronized_实现原理">35. synchronized 实现原理</a></li>
<li><a href="#_什么是aqs">36. 什么是AQS</a></li>
<li><a href="#_aqs_唤醒节点_为何从后往前找">37. AQS 唤醒节点，为何从后往前找</a></li>
<li><a href="#_synchronized_和_reentrantlock_的区别">38. synchronized 和 ReentrantLock 的区别</a></li>
<li><a href="#_reentrantreadwritelock_实现原理">39. ReentrantReadWriteLock 实现原理</a></li>
<li><a href="#_jdk_提供了几种线程池">40. JDK 提供了几种线程池</a></li>
<li><a href="#_线程池的核心参数有什么">41. 线程池的核心参数有什么？</a></li>
<li><a href="#_线程池的状态">42. 线程池的状态</a></li>
<li><a href="#_线程池添加工作线程的流程">43. 线程池添加工作线程的流程</a></li>
<li><a href="#_线程池为何要构建空任务的非核心线程">44. 线程池为何要构建空任务的非核心线程？</a></li>
<li><a href="#_线程池使用完后为何必须_shutdown">45. 线程池使用完后为何必须 shutdown ？</a></li>
<li><a href="#_线程池的核心参数如何设置">46. 线程池的核心参数如何设置</a></li>
<li><a href="#_concurrenthashmap_优化点">47. ConcurrentHashMap 优化点</a></li>
<li><a href="#_concurrenthashmap_散列算法">48. ConcurrentHashMap 散列算法</a></li>
<li><a href="#_concurrenthashmap_初始化数组的流程">49. ConcurrentHashMap 初始化数组的流程</a></li>
<li><a href="#_concurrenthashmap_扩容流程">50. ConcurrentHashMap 扩容流程</a></li>
<li><a href="#_concurrenthashmap_读取数据的流程">51. ConcurrentHashMap 读取数据的流程？</a></li>
<li><a href="#_concurrenthashmap_计数器的实现">52. ConcurrentHashMap 计数器的实现</a></li>
</ul>
</li>
<li><a href="#_mysql">MySQL</a>
<ul class="sectlevel1">
<li><a href="#_什么是_buffer_pool">53. 什么是 Buffer Pool ？</a></li>
<li><a href="#_innodb_如何管理_page_页">54. InnoDB 如何管理 Page 页?</a></li>
<li><a href="#_mysql_为什么要改进_lru_算法">55. MySQL 为什么要改进 LRU 算法?</a></li>
<li><a href="#_使用索引一定可以提高效率吗">56. 使用索引一定可以提高效率吗？</a></li>
<li><a href="#_介绍一下_page_页的结构">57. 介绍一下 Page 页的结构</a></li>
<li><a href="#_说以下聚簇索引和非聚簇索引">58. 说以下聚簇索引和非聚簇索引</a></li>
<li><a href="#_索引有那几种类型">59. 索引有那几种类型</a></li>
<li><a href="#_介绍一下最佳左前缀法则">60. 介绍一下最佳左前缀法则</a></li>
<li><a href="#_什么是索引下推">61. 什么是索引下推</a></li>
<li><a href="#_什么是自适应哈希索引">62. 什么是自适应哈希索引</a></li>
<li><a href="#_为什么_like_以_开头索引会失效">63. 为什么 like 以 % 开头索引会失效</a></li>
<li><a href="#_自增还是uuid">64. 自增还是UUID</a></li>
<li><a href="#_innodb_和_myisam_的区别">65. InnoDB 和 MyISAM 的区别</a></li>
<li><a href="#_b_树和_b_树的区别">66. B 树和 B+ 树的区别</a></li>
<li><a href="#_为什么采用_b_树而不是_b_树">67. 为什么采用 B+ 树而不是 B 树</a></li>
<li><a href="#_innodb中的_b_树是怎么产生的">68. Innodb中的 B+ 树是怎么产生的？</a></li>
<li><a href="#_一个b_树大概能存放多少条索引记录">69. 一个B+树大概能存放多少条索引记录</a></li>
<li><a href="#_explain">70. explain</a></li>
<li><a href="#_如何进行分页查询优化">71. 如何进行分页查询优化</a></li>
<li><a href="#_如何做慢查询优化">72. 如何做慢查询优化</a></li>
<li><a href="#_hash_索引的优缺点">73. Hash 索引的优缺点</a></li>
<li><a href="#_说一下_innodb_内存相关的参数优化">74. 说一下 InnoDB 内存相关的参数优化</a></li>
<li><a href="#_innodb_日志相关参数优化">75. InnoDB 日志相关参数优化</a></li>
<li><a href="#_innodb_io线程相关的参数优化">76. InnoDB io线程相关的参数优化？</a></li>
<li><a href="#_什么是写失效">77. 什么是写失效？</a></li>
<li><a href="#_如何进行join优化">78. 如何进行join优化</a></li>
<li><a href="#_索引那些情况会失效">79. 索引那些情况会失效</a></li>
<li><a href="#_什么是覆盖索引">80. 什么是覆盖索引</a></li>
<li><a href="#_mysql_事务的特性">81. MySQL 事务的特性</a></li>
<li><a href="#_mysql_可重复读是怎么实现的">82. MySQL 可重复读是怎么实现的？</a></li>
<li><a href="#_repeatable_read_解决了幻读问题嘛">83. Repeatable Read 解决了幻读问题嘛？</a></li>
<li><a href="#_数据库锁的种类">84. 数据库锁的种类</a></li>
<li><a href="#_请说下共享锁和排他锁">85. 请说下共享锁和排他锁</a></li>
<li><a href="#_innodb_的行锁时怎么实现的">86. InnoDB 的行锁时怎么实现的？</a></li>
<li><a href="#_并发事务会产生那些问题">87. 并发事务会产生那些问题？</a></li>
<li><a href="#_mvcc_内部细节">88. mvcc 内部细节</a></li>
<li><a href="#_说一下_mysql_死锁的原因和处理方法">89. 说一下 MySQL 死锁的原因和处理方法</a></li>
<li><a href="#_mysql_体系结构">90. MySQL 体系结构</a></li>
<li><a href="#_undo_log_redo_log_bin_log">91. undo log、redo log、bin log</a></li>
<li><a href="#_说一下_redo_log_和_undo_log_的持久化策略">92. 说一下 redo log 和 undo log 的持久化策略</a></li>
<li><a href="#_mysql_在线修改大表影响">93. MySQL 在线修改大表影响</a></li>
<li><a href="#_count_列名_count_1_count_区别">94. count(列名) count（1） count(*) 区别</a></li>
<li><a href="#_什么时候进行分库分表">95. 什么时候进行分库分表？</a></li>
<li><a href="#_说说_mysql_的主从复制">96. 说说 Mysql 的主从复制</a></li>
<li><a href="#_sql执行流程">97. sql执行流程</a></li>
<li><a href="#_mysql_支持缓存查询嘛">98. Mysql 支持缓存查询嘛？</a></li>
<li><a href="#_acid_靠什么保证">99. ACID 靠什么保证？</a></li>
<li><a href="#_mysql_主从同步原理">100. MySQL 主从同步原理</a></li>
</ul>
</li>
<li><a href="#_redis">Redis</a>
<ul class="sectlevel1">
<li><a href="#_redis_为什么这么快">101. redis 为什么这么快</a></li>
<li><a href="#_redis_使用场景">102. redis 使用场景</a></li>
<li><a href="#_redis_为什么不采用多线程">103. redis 为什么不采用多线程</a></li>
<li><a href="#_redis_6_0_之后为何引入多线程">104. redis 6.0 之后为何引入多线程</a></li>
<li><a href="#_redis_有哪些高级功能">105. redis 有哪些高级功能</a></li>
<li><a href="#_为什么要用redis">106. 为什么要用redis</a></li>
<li><a href="#_redis_和_memcached_相对比较有什么优势">107. redis 和 memcached 相对比较有什么优势</a></li>
<li><a href="#_怎么理解_redis_中的事务">108. 怎么理解 Redis 中的事务</a></li>
<li><a href="#_redis_的过期策略以及内存淘汰机制">109. redis 的过期策略以及内存淘汰机制</a></li>
<li><a href="#_什么是缓存穿透_如何避免">110. 什么是缓存穿透？如何避免？</a></li>
<li><a href="#_什么是缓存雪崩_如何避免">111. 什么是缓存雪崩？如何避免？</a></li>
<li><a href="#_使用redis如何设计分布式锁">112. 使用redis如何设计分布式锁</a></li>
<li><a href="#_怎么使用_redis_实现消息队列">113. 怎么使用 redis 实现消息队列？</a></li>
<li><a href="#_什么是_bigkey_会有什么影响">114. 什么是 bigkey？ 会有什么影响？</a></li>
<li><a href="#_redis_如何解决_key_冲突">115. redis 如何解决 key 冲突</a></li>
<li><a href="#_在怎么提高缓存命中率">116. 在怎么提高缓存命中率</a></li>
<li><a href="#_redis_的持久化">117. redis 的持久化</a></li>
<li><a href="#_为什么_redis_需要把所有的数据放到内存中">118. 为什么 redis 需要把所有的数据放到内存中？</a></li>
<li><a href="#_如何保证缓存与数据库双写数据一致性">119. 如何保证缓存与数据库双写数据一致性？</a></li>
<li><a href="#_redis_集群方案应该怎么做">120. redis 集群方案应该怎么做</a></li>
<li><a href="#_redis_集群方案中什么情况导致整个集群不可用">121. redis 集群方案中什么情况导致整个集群不可用？</a></li>
<li><a href="#_说一说_redis_哈希槽的概念">122. 说一说 redis 哈希槽的概念</a></li>
<li><a href="#_redis_集群会有写操作丢失嘛_为什么">123. redis 集群会有写操作丢失嘛？为什么？</a></li>
<li><a href="#_什么是热点数据_什么是冷数据">124. 什么是热点数据，什么是冷数据？</a></li>
<li><a href="#_什么情况会导致_redis_阻塞">125. 什么情况会导致 Redis 阻塞?</a></li>
</ul>
</li>
<li><a href="#_spring_相关">Spring 相关</a>
<ul class="sectlevel1">
<li><a href="#_谈谈你对spring的理解">126. 谈谈你对Spring的理解</a></li>
<li><a href="#_spring_中应用了那些设计模式">127. Spring 中应用了那些设计模式</a></li>
<li><a href="#_autowired_和_resource_的区别">128. Autowired 和 Resource 的区别</a></li>
<li><a href="#_spring_常用的注解">129. spring 常用的注解</a></li>
<li><a href="#_循环依赖的理解">130. 循环依赖的理解</a></li>
<li><a href="#_spring_如何解决循环依赖">131. Spring 如何解决循环依赖</a></li>
<li><a href="#_spring_三层缓存">132. Spring 三层缓存</a></li>
<li><a href="#_spring_bean_的生命周期">133. Spring Bean 的生命周期</a></li>
<li><a href="#_spring支持几种作用域">134. Spring支持几种作用域</a></li>
<li><a href="#_spring_事务的隔离级别">135. spring 事务的隔离级别</a></li>
<li><a href="#_事务的传播行为">136. 事务的传播行为</a></li>
<li><a href="#_spring_事务的实现方式">137. spring 事务的实现方式</a></li>
<li><a href="#_spring_事务的本质以及失效">138. spring 事务的本质以及失效</a></li>
<li><a href="#_beanfactory_applicationcontext_理解">139. BeanFactory ApplicationContext  理解</a></li>
<li><a href="#_beanfactorypostprocessor_理解">140. BeanFactoryPostProcessor 理解</a></li>
<li><a href="#_beanpostprocessor">141. BeanPostProcessor</a></li>
<li><a href="#_谈谈_springmvc_的理解">142. 谈谈 SpringMVC 的理解</a></li>
<li><a href="#_谈谈_spring_和_springmvc_的关系">143. 谈谈 Spring 和 SpringMVC 的关系</a></li>
<li><a href="#_谈谈_delegatingfilterproxy_的理解">144. 谈谈 DelegatingFilterProxy 的理解</a></li>
<li><a href="#_谈谈_springboot_的自动装配原理">145. 谈谈 SpringBoot 的自动装配原理</a></li>
<li><a href="#_谈谈你对_import_注解的理解">146. 谈谈你对 Import 注解的理解</a></li>
<li><a href="#_谈谈你对_deferredimportselector_的理解">147. 谈谈你对 DeferredImportSelector 的理解</a></li>
<li><a href="#_谈谈_bottstrap_yml_的理解">148. 谈谈 bottstrap.yml 的理解</a></li>
<li><a href="#_如果要对属性文件中的账号密码加密如何实现">149. 如果要对属性文件中的账号密码加密如何实现？</a></li>
<li><a href="#_谈谈_indexed_注解的作用">150. 谈谈 Indexed 注解的作用</a></li>
<li><a href="#__component_controller_repository_service_的区别">151. @Component @Controller @Repository @Service 的区别</a></li>
<li><a href="#_aop_有哪些通知类型">152. aop 有哪些通知类型</a></li>
<li><a href="#_介绍下_spring_中的依赖注入">153. 介绍下 Spring 中的依赖注入</a></li>
<li><a href="#_spring_中的单例_bean_是线程安全的吗">154. Spring 中的单例 Bean 是线程安全的吗？</a></li>
<li><a href="#__componentscan_注解是干什么的">155. @ComponentScan 注解是干什么的？</a></li>
<li><a href="#__enableautoconfiguration_注解是干什么的">156. @EnableAutoConfiguration 注解是干什么的？</a></li>
<li><a href="#__import_注解的三种用法">157. @Import 注解的三种用法</a></li>
<li><a href="#_requestmapping_和_getmapping_的区别">158. RequestMapping 和 GetMapping 的区别</a></li>
<li><a href="#_springboot_的核心注解是那个_主要由那几个注解组成">159. SpringBoot 的核心注解是那个？主要由那几个注解组成？</a></li>
<li><a href="#_springboot_可以兼容老项目吗">160. SpringBoot 可以兼容老项目吗？</a></li>
<li><a href="#_springboot_如何定义多套不同环境的配置">161. SpringBoot 如何定义多套不同环境的配置</a></li>
<li><a href="#_springboot_需要独立的容器吗">162. SpringBoot 需要独立的容器吗？</a></li>
<li><a href="#_springboot_有哪几种读取配置的方式">163. SpringBoot 有哪几种读取配置的方式？</a></li>
<li><a href="#_springboot_支持那些日志框架">164. SpringBoot 支持那些日志框架</a></li>
<li><a href="#_springboot_springmvc_spring_有什么区别">165. SpringBoot SpringMVC Spring 有什么区别？</a></li>
<li><a href="#_springboot_的监视器是什么">166. SpringBoot 的监视器是什么？</a></li>
<li><a href="#_springboot_打包成_jar_和普通的_jar_有什么区别">167. SpringBoot 打包成 jar 和普通的 jar 有什么区别？</a></li>
<li><a href="#_springboot_的_run_方法做了什么">168. SpringBoot 的 run 方法做了什么？</a></li>
<li><a href="#_springboot_的优点">169. SpringBoot 的优点</a></li>
<li><a href="#_springboot_如何解决跨域问题">170. SpringBoot 如何解决跨域问题</a></li>
<li><a href="#_springboot_如何配置_log4j">171. SpringBoot 如何配置 log4j</a></li>
<li><a href="#_springboot_如何实现定时任务">172. SpringBoot 如何实现定时任务</a></li>
<li><a href="#_springboot_自动装配的核心配置文件有哪些">173. SpringBoot 自动装配的核心配置文件有哪些？</a></li>
<li><a href="#_springboot_自动装配的流程是怎么样的">174. SpringBoot 自动装配的流程是怎么样的？</a></li>
<li><a href="#_介绍几个常用的_starter">175. 介绍几个常用的 starter</a></li>
<li><a href="#_你如何理解_springboot_配置加载顺序">176. 你如何理解 SpringBoot 配置加载顺序</a></li>
<li><a href="#_如何实现_springboot_应用程序的安全性">177. 如何实现 SpringBoot 应用程序的安全性</a></li>
<li><a href="#_如何在_springboot_启动的时候运行一些特点的代码">178. 如何在 SpringBoot 启动的时候运行一些特点的代码</a></li>
<li><a href="#_如何重新加载_springboot_的更改_而无需重新启动服务器">179. 如何重新加载 SpringBoot 的更改，而无需重新启动服务器</a></li>
<li><a href="#_什么是_springboot_starter">180. 什么是 SpringBoot starter？</a></li>
<li><a href="#_什么是_springboot">181. 什么是 SpringBoot？</a></li>
<li><a href="#_我们如何连接一个像_mysql_的外部数据库">182. 我们如何连接一个像 MySQL 的外部数据库</a></li>
<li><a href="#_运行_springboot_项目的方式">183. 运行 SpringBoot 项目的方式</a></li>
</ul>
</li>
</ul>
</div>
</div>
<div id="content">
<h1 id="_分布式" class="sect0">分布式</h1>
<div class="sect1">
<h2 id="_分布式幂等性如何设计">1. 分布式幂等性如何设计？</h2>
<div class="sectionbody">
<div class="paragraph">
<p>在高并发的场景里，一般需要对插入、更新进行幂等处理，意味这多次同样的请求，只有完成一次。</p>
</div>
<div class="paragraph">
<p>查询和删除不存在幂等说法，查询多次都是没有问题的，第一次删除、后面再次删除，返回的也是0，也是成功的。</p>
</div>
<div class="ulist">
<ul>
<li>
<p>token 方案，生成唯一的</p>
</li>
<li>
<p>添加分布式锁</p>
</li>
<li>
<p>唯一索引以及去重表</p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_说说你对分布式事务的了解">2. 说说你对分布式事务的了解？</h2>
<div class="sectionbody">
<div class="paragraph">
<p>可以从 4 个点进行说明：场景、事务ACID、分布式理论CAP以及解决方案</p>
</div>
<div class="ulist">
<ul>
<li>
<p>场景：存在多个服务，每个服务操作库，都必须保证同时成功，否则全部失败。</p>
</li>
<li>
<p>ACID: 分布式服务部署的情况，需要考虑所有设计到的服务的事务加在一起，保证ACID。</p>
</li>
<li>
<p>CAP 理论：介绍下 CAP。</p>
</li>
<li>
<p>方案：可采用 Seata，消息队列加本地事务表，事务消息，最大努力通知方案，tcc</p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_什么是_2_阶段提交协议_2pc">3. 什么是 2 阶段提交协议？ 2pc</h2>
<div class="sectionbody">
<div class="paragraph">
<p>流程：第一阶段询问各个事务数据源是否准备好，第二阶段才真正将数据提交给数据源。适用于数据强一致性要求很高的场景、经理保证数据强一致性，不能100%保证，但是对性能影响较大，不适合高并发高性能场景。</p>
</div>
<div class="paragraph">
<p>遇到的问题：
- 性能问题，所有的参与者处于同步阻塞状态，占用系统资源，容易导致性能瓶颈。
- 可靠性问题：如果协调者存在单点故障问题，或出现故障，提供者将一直处于锁定状态。
- 数据一致性问题： 在第二阶段，有可能存在提交指令未执行问题，导致数据不一致。</p>
</div>
<div class="paragraph">
<p>对流程的优化：
实际使用：seata、tcc</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_什么是补偿事务">4. 什么是补偿事务？</h2>
<div class="sectionbody">
<div class="paragraph">
<p>针对每个操作，都要注册一个与其对应的确认和补偿操作。TCC就是采用补偿机制。</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_如何用消息队列实现分布式事务的">5. 如何用消息队列实现分布式事务的？</h2>
<div class="sectionbody">
<div class="paragraph">
<p>通过消息队列，异步执行不同服务，保证最终一致性，也即BASE理论，保证每个服务都是本地事务。给用户响应够快、需要考虑补偿机制。</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_分布式_id_生成有几种方案">6. 分布式 ID 生成有几种方案？</h2>
<div class="sectionbody">
<div class="ulist">
<ul>
<li>
<p>UUID：本地生成、简单、性能好、没有冲突风险，但是长度过长、无序且不可读，查询效率低</p>
</li>
<li>
<p>数据库自增ID: 数据库设置为自增，设置不同的步长，优点是实现简单、id有序，但是步长依赖于数据库数量，不方便数据库扩展。</p>
</li>
<li>
<p>批量生成ID：</p>
</li>
<li>
<p>redis生成ID：通过自增命令</p>
</li>
<li>
<p>雪花算法：1个符号位41位时间戳10位机器位12位序列号，共64位，高性能、按时间有序，一般不会碰状态，需要独立开发部署，百度、美团Leaf</p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_常见的负载均衡算法有那些">7. 常见的负载均衡算法有那些？</h2>
<div class="sectionbody">
<div class="ulist">
<ul>
<li>
<p>轮询均衡算法：适合服务器硬件都相同，一个个来。</p>
</li>
<li>
<p>加权轮询算法：按照权重不同来分发，基于配置。</p>
</li>
<li>
<p>随机轮询算法：</p>
</li>
<li>
<p>最少连接：记录每个服务正在处理的连接数，给最少连接的服务器上。</p>
</li>
<li>
<p>原地址散列：根据来源ip进行hash，只要地址不变，后面所有的请求，都是同一个节点处理，有利于session信息的维护。</p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_什么是计数器_固定窗口_算法">8. 什么是计数器（固定窗口）算法？</h2>
<div class="sectionbody">
<div class="paragraph">
<p>指定时间周期内，累加访问的次数，达到设定的阈值，触发限流策略。存在临界问题，比如在设在1分钟限定100，在59秒来了60个，第二分钟10秒又来了60个，
在这段时间，请求数量超过了100。</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_什么是滑动窗口算法">9. 什么是滑动窗口算法？</h2>
<div class="sectionbody">
<div class="paragraph">
<p>为了解决固定窗口的缺点。在时间周期内进行分段，每个小的时间窗口内固定处理一定的请求，窗口随着时间走并删除过期的时间窗口，保证窗口内的请求数量不超过阈值，分得越细，越平滑。</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_什么是漏桶算法">10. 什么是漏桶算法？</h2>
<div class="sectionbody">
<div class="paragraph">
<p>有恒定的流出速度，不管流入的速度有多快，流出的速度始终不变。类似于队列。缺点是，短时间大流量，处理不了。</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_什么是令牌桶算法">11. 什么是令牌桶算法？</h2>
<div class="sectionbody">
<div class="paragraph">
<p>增加一个固定大小的容器，也就是令牌桶，系统恒定的速率生成令牌，并放入桶中。请求来了，如果能获取到令牌则可以执行。</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_数据库如何处理大数据量">12. 数据库如何处理大数据量？</h2>
<div class="sectionbody">
<div class="paragraph">
<p>分区、分库分表、主从架构读写分离。</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_什么是cap理论">13. 什么是CAP理论？</h2>
<div class="sectionbody">
<div class="ulist">
<ul>
<li>
<p>C: consistency,一致性，数据在多个副本中保持一致</p>
</li>
<li>
<p>A: avaliable，可用性</p>
</li>
<li>
<p>P: partition,分区容错性。</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>在分布式系统中，必须满足P，也即分区容错性，如果不存在分区故障，那么AC都是成立的，当P满足时，A和C是互斥的，只能保证一个。</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_什么是_base_理论">14. 什么是 BASE 理论？</h2>
<div class="sectionbody">
<div class="paragraph">
<p>由于CAP理论中C和A无法同时满足，则提出BASE理论，保证基本可用、能保证最终一致性，不需要实时保证系统数据的强一致性。
实际中产品的可用性比强一致性更重要。</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_什么是可靠消息最终一致性方案">15. 什么是可靠消息最终一致性方案？</h2>
<div class="sectionbody">
<div class="paragraph">
<p>允许数据在业务中出现短暂的不一致状态。</p>
</div>
</div>
</div>
<h1 id="_消息中间件" class="sect0">消息中间件</h1>
<div class="sect1">
<h2 id="_为什么使用消息队列_mq">16. 为什么使用消息队列 MQ ?</h2>
<div class="sectionbody">
<div class="paragraph">
<p>消息队列的常见使用场景有很多，比较核心的有3个，解耦、异步、削峰。</p>
</div>
<div class="paragraph">
<p>进行放款申请时，其中有一个步骤需要生成合同成功后，需要发送一条短信给客户，让客户签署。
通过加入消息队列，异步的形式发送短信，以提升业务处理速度。即使发送短信服务不可用，也可等待其服务可用时，重新从消息队列里获取信息，发送短信，不会阻塞主业务。
除此之外，除了发送短信，如果需要增加其他的业务，比如站内信服务提醒，可以随时加入进行消息消费。
还有就是，业务系统在每个时间点处接受到的请求数量不一样，高峰期，服务器处理能力并不能应对突然的流量，可以通过时间换取空间，将
需要处理的业务放入消息队列中，让服务器慢慢消费，处理，达到平缓处理突发请求的效果。</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_如何选择消息队列">17. 如何选择消息队列</h2>
<div class="sectionbody">
<div class="paragraph">
<p>producers &#8594; exchanges &#8594; queues &#8594; consumers</p>
</div>
<div class="paragraph">
<p>rabbitMq: 稳定性,可靠性高。适合对数据一致性要求较高的场景。</p>
</div>
<div class="paragraph">
<p>kafka: 分布式消息发布订阅系统，性能高效，可扩展良好并且可持久化，兼容性比较好，在大数据领域，优先支持，极限处理能力可以超过每秒2k万条消息，缺点是延迟比比较高，topic 过多，性能会下降。</p>
</div>
<div class="paragraph">
<p>RocketMQ: Java开发，参考kafka，性能、稳定性、 可靠性值得信赖，中文设计，持续发展，Java开发，扩展和2次开发容易</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_如何保证消息不丢失">18. 如何保证消息不丢失？</h2>
<div class="sectionbody">
<div class="ulist">
<ul>
<li>
<p>确保消息到 MQ</p>
</li>
<li>
<p>确保消息路由到正确的队列</p>
</li>
<li>
<p>确保消息在队列中正确的存储</p>
</li>
<li>
<p>确保消息从队列中正确的投递给消费者</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>解决方案：</p>
</div>
<div class="ulist">
<ul>
<li>
<p>发送方确认模式 设置连接工厂时，需要打开消息确认开关，在 rabbittemplate中需要设置一个回调函数，进行 ack 确认处理</p>
</li>
<li>
<p>路由失败通知，打开通知 Mandatory，开启失败通知，再设置一个回调方法，失败通知执行方法。</p>
</li>
<li>
<p>交换器、队列、消息都需要持久化</p>
</li>
<li>
<p>消费者手动确认消费消息，配置消息容器，开启手动确认。</p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_什么是消息重复">19. 什么是消息重复？</h2>
<div class="sectionbody">
<div class="paragraph">
<p>发送时重复
- 网络
- 卡顿
- 本身</p>
</div>
<div class="paragraph">
<p>消费重复</p>
</div>
<div class="ulist">
<ul>
<li>
<p>应用处理发送问题，没有发出确认</p>
</li>
<li>
<p>应用处理完成，网络出现问题</p>
</li>
<li>
<p>应用处理超时</p>
</li>
<li>
<p>消息中间件出问题，宕机，重启后，又重发</p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_如何解决mq中的重复消息">20. 如何解决MQ中的重复消息？</h2>
<div class="sectionbody">
<div class="paragraph">
<p>主要要求接受者来处理这种重复情况，要求消费者的消息处理是幂等操作。</p>
</div>
<div class="paragraph">
<p>去重表，利用数据库特性，设计一个唯一性的键，例如id，当消息来时，先插入到表中，如果不存在，则进行业务处理，并确认消息消费，如果存在，则捕获异常，直接确认返回。适用于分布式，在各个服务可以针对业务不同单独设计一张这种表来处理消息重复问题。</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_消息队列的优缺点_使用场景">21. 消息队列的优缺点、使用场景</h2>
<div class="sectionbody">
<div class="paragraph">
<p>优点：
- 解耦
- 异步处理
- 削峰</p>
</div>
<div class="paragraph">
<p>缺点
- 增加系统复杂度，需要处理幂等、重复消费、消息丢失等问题
- 一致性，消费端可能失败
- 可用性降低，mq故障会影响系统可用</p>
</div>
<div class="paragraph">
<p>场景： 日志采集、发布订阅等</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_如何保证消息可靠传输">22. 如何保证消息可靠传输</h2>
<div class="sectionbody">
<div class="ulist">
<ul>
<li>
<p>不能重复消息</p>
</li>
<li>
<p>消息成功发送</p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_死信队列_延时队列">23. 死信队列、延时队列</h2>
<div class="sectionbody">
<div class="paragraph">
<p>死信队列，也是一个消息队列，只是存放那些没有成功消费的消息，延长消息的生命周期。
延时队列，指定需要在多久后才能被消费，用来处理一些过期新的操作业务，比如 10 分钟内未支付则取消订单。</p>
</div>
</div>
</div>
<h1 id="_java_基础相关" class="sect0">Java 基础相关</h1>
<div class="sect1">
<h2 id="_java中线程的实现方式">24. Java中线程的实现方式</h2>
<div class="sectionbody">
<div class="ulist">
<ul>
<li>
<p>继承 Thread 类，重写 run 方法，创建对象，调用 start 方法</p>
</li>
<li>
<p>实现 Runable 接口，实现 run 方法，通过 Thread 构造函数构造，调用 start 方法</p>
</li>
<li>
<p>实现 Callable 重写 call 方法，将其对象传入 FutureTask 对象中，然后传入 Thread 对象中，调用 start 方法</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>底层只有一种，实现 Runable 接口。</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_java中线程的状态">25. Java中线程的状态</h2>
<div class="sectionbody">
<div class="ulist">
<ul>
<li>
<p>NEW(新建)</p>
</li>
<li>
<p>RUNABLE(就绪/运行)</p>
</li>
<li>
<p>BLOCKED(阻塞，获取不到锁)</p>
</li>
<li>
<p>WAITING(等待唤醒)</p>
</li>
<li>
<p>TIMED_WAITING(超时自动唤醒)</p>
</li>
<li>
<p>TERMINATED(终止)</p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_如何停止线程">26. 如何停止线程</h2>
<div class="sectionbody">
<div class="ulist">
<ul>
<li>
<p>通过 stop 函数，已弃用，不推荐使用</p>
</li>
<li>
<p>设置共享变量，子线程中判断，然后结束 run 方法</p>
</li>
<li>
<p>(推荐) 调用 interrupt 方法，中断标记 true/false,默认 false。</p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_java中_sleep_和_wait_方法区别">27. Java中 sleep 和 wait 方法区别</h2>
<div class="sectionbody">
<div class="ulist">
<ul>
<li>
<p>sleep 属于线程的静态方法， wait属于 Object 类的方法</p>
</li>
<li>
<p>sleep 属于 TIMED_WAITING ，自动被唤醒；wait 属于 WAITING，需要手动唤醒</p>
</li>
<li>
<p>sleep 在持有锁的时候执行，不会释放锁资源；wait 在执行后，会释放锁资源</p>
</li>
<li>
<p>sleep 有锁无锁都可以执行；wait 必须要在有锁的情况下执行</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>wait 方法会将持有锁的线程从 owner 放到 WaitSet 集合中，这个操作是在修改 ObjectMonitor 对象，如果没有持有 synchronized 锁，是无法操作 ObjectMonitor 对象的。</p>
</div>
<div class="paragraph">
<p>扩展点：ObjectMonitor、对象锁、锁的本质底层实现</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_java_并发编程的三大特性">28. Java 并发编程的三大特性</h2>
<div class="sectionbody">
<div class="paragraph">
<p>原子性：一个操作是不可分割的，不可中断的，一个线程执行时，另一个线程不会影响它。
- synchronized 拿到锁才能执行，线程之间互斥
- CAS 乐观锁，首先查看内存中的值是否和预期值一致，如果一致，则执行替换操作，这是一个原子性操作
- Lock 锁 lock()、unlock()</p>
</div>
<div class="paragraph">
<p>可见性：保证多线程获取到的都是最新的值。这是由于 JMM 影响，线程会操作数据都是在本地内存中，如果一个线程执行完后，
- volatile 内存屏障，读写都是在主内存内，读的时候，告诉 cpu 不从缓存中获取数据，直接从主内存中获取值；写的时候，JMM 会将 cpu 缓存即使刷新到主内存中
- synchronized 执行那一刻保证可见性：在执行加锁的过程中，会将内部涉及到的变量从 CPU 内存中移除，必须从主内存中重新拿数据，释放锁之后，会将CPU缓存中的数据同步到主内存。
- lock CAS 加操作一个 volatile 修饰的变量，也是在执行的那一刻保证可见性。
- final 运行时期不可修改，则多线程来获取肯定是一样，并不是每次都要从主内存中获取。</p>
</div>
<div class="paragraph">
<p>有序性：编译时，在不影响最终结果的情况下，为提高效率，允许指令进行重排序。
- volatile 内存屏障，在指令之间添加一道指令，这个指令可以避免执行其他指令的重新排序，禁止重排序，保证多线程情况下，其他线程获取到的是完整的数据。</p>
</div>
<div class="paragraph">
<p>扩展点：
- JMM 内存模型
- 如果对 volatile 修饰的属性进行写操作，会生成到 lock 前缀的指令，cpu 执行修改时，会将缓存立即同步到主内存中，
还会将其他线程中的本地内存这个数据设置为无效，必须重新从主内存中拉取。</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_什么是cas_有什么特性">29. 什么是CAS，有什么特性？</h2>
<div class="sectionbody">
<div class="paragraph">
<p>compare and swap 他在替换内存中的某个值时，首先查看内存中的值是否和预期的值一直，如果一致，则执行替换操作，这个是原子性操作。
实现是Java中的Unsafe类，JVM会帮助我们将方法实现CAS汇编指令</p>
</div>
<div class="ulist">
<ul>
<li>
<p>不会带来线程的挂起和唤醒，带来用户态和内核态切换带来的性能消耗，缺点是，ABA问题，自旋时间过程（自旋次数限制），性能消耗严重</p>
</li>
<li>
<p>但是会产生 ABA 问题，可以通过版本号机制避免 ABA 问题。</p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect1">
<h2 id="__content_注解的作用">30. @Content 注解的作用</h2>
<div class="sectionbody">
<div class="paragraph">
<p>cpu 缓存是以缓存行进行存储的，如果缓存行中某个数据发生变化，缓存行其他的数据都需要去主内存同步，
加上这个注解，会将缓存行的空闲位置填充没有意义的数据，避免同步数据消耗的时间。</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_java_中的四种引用类型">31. Java 中的四种引用类型</h2>
<div class="sectionbody">
<div class="ulist">
<ul>
<li>
<p>强引用，一般程序创建对象，然后复制给一个引用变量，那这个引用变量就是一个强引用。如果一个对象被强引用变量引用时，始终处于可达状态，那么不会被垃圾回收机制回收的，即使这个对象以后都没有被使用到，造成OOM的主要原因之一</p>
</li>
<li>
<p>软引用，一般通过 SoftRefference，系统内存不足时，会被回收，一般作为缓存，比如处理文件资源，可以先全部缓存，进行处理</p>
</li>
<li>
<p>弱引用，weakRefference, 生命周期更短，垃圾回收机制一运行，不管内存是否足够，都会回收。</p>
</li>
<li>
<p>虚应用，主要作用用户跟踪对象被垃圾回收的状态，开发中一般用不到。</p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_threadlocal_是什么以及内存泄漏问题">32. ThreadLocal 是什么以及内存泄漏问题</h2>
<div class="sectionbody">
<div class="paragraph">
<p>每个 Thread 中都有一个成员变量 ThreadLocalMap，ThreadLocal 本身不存储数据，基于 ThreadLocal 去操作 ThreadLocalMap
ThreadLocalMap是基于 Entry[] 实现的，一个线程可以绑定多个 ThreadLocal ,可能需要存储多个数据，所以采用 Entry[] 的形式实现
每个线程都有独立的 ThreadLocalMap，ThreadLocal 对象作为 Key,对 value 进行存取。
ThreadLocalMap 中 key 是弱引用，弱引用的特点是，gc时，会进行回收，设计成弱引用是当ThreadLocal对象失去强引用后，这个ThreadLocal对象可以被回收，这是设计层面的考虑，可以避免OOM</p>
</div>
<div class="paragraph">
<p>ThreadLocal 内存泄漏问题：
如果 ThreadLocal 对象应用丢失，key 因为是弱引用，会被GC回收，但是如果线程没有被回收，内存中的 value 无法被回收，也无法被获取到，因此 value 会导致内存泄漏
解决办法是在使用完 ThreadLocal 对象后，调用remove方法，移除Entry即可</p>
</div>
<div class="paragraph">
<p>使用场景：
- spring 事务管理，保证所有的事务都使用同一个数据库连接，就是通过 ThreadLocal 方式</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_java_中锁的分类">33. Java 中锁的分类</h2>
<div class="sectionbody">
<div class="paragraph">
<p>重入锁和不可重入锁：
重入锁：synchronized ReentrantLock 当一个线程获取锁后，可以在当前线程再次获取这个锁，反之则是不可重入锁（线程池中的worker就是不可重入锁）</p>
</div>
<div class="paragraph">
<p>乐观锁和悲观锁
synchromized、ReentrantLoc k都是悲观锁，当线程获取不到锁时，会将当前线程挂起，挂起会涉及到内核态和用户态的切换，是比较耗资源的。
用户态：JVM 可以自行执行的指令，不需要借助操作系统执行
内核态：JVM 不可以执行的指令，需要借助操作系统执行
乐观锁： CAS 操作就是乐观锁的一种实现，当获取不到锁资源，可以让 cpu 再次调度，尝试获取锁资源 Atomic 原子类中，就是基于 CAS 乐观锁实现的</p>
</div>
<div class="paragraph">
<p>公平锁和非公平锁：
synchronized只能是非公平锁
Lock可以实现公平锁和非公平锁
公平锁：当线程获取锁资源，需要排队获取。
非公平锁：A拿到锁资源，B也在排队获取，C来获取锁时，会先尝试，如果获取到锁，那么插队成功，如果没有依旧要排队到B后面，等B拿到或者取消后，才可以尝试获取锁。</p>
</div>
<div class="paragraph">
<p>互斥锁和共享锁：
互斥锁：同一时间点，只有一个线程拥有互斥锁，Synchronized、ReentrantLock 都是互斥锁
共享锁：同一时间点，当前共享锁可以被多个线程持有 ReentrantReadWriteLock 有互斥锁也有共享锁</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_synchronized_在1_6的优化">34. synchronized 在1.6的优化</h2>
<div class="sectionbody">
<div class="paragraph">
<p>锁消除，如果修饰的代码中不存在临界资源，加或不加没有区别，会触发锁消除，即便用了，也不会触发。</p>
</div>
<div class="paragraph">
<p>锁膨胀，如果在一个循环中频繁的获取和释放资源，消耗很大，锁膨胀就是将锁的范围扩大，避免频繁的竞争带来不必要的消耗。</p>
</div>
<div class="paragraph">
<p>锁升级，在1.6之前，如果获取不到锁就直接挂起线程，性能较差，1.6之后，做了几个分别
- 无锁、匿名偏向：当前对象没有作为锁的存在
- 偏向锁：如果当前资源只有一个线程在频繁的获取和释放，那么当这个线程过来只需要判断是否是当前线程，如果是，那么直接获取，如果不是，基于CAS方式，尝试将偏向锁指向当前线程,如果获取不到，会进行锁升级，升级为轻量级锁
- 轻量级锁：会采用自旋锁的方式去频繁的以CAS形式获取锁资源，如果自旋一定次数，获取不到，则进行锁升级
- 重量级锁：传统的锁方式，拿不到资源，就挂起当前线程，涉及到用户态和内核态的切换。</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_synchronized_实现原理">35. synchronized 实现原理</h2>
<div class="sectionbody">
<div class="paragraph">
<p><a href="https://zhuanlan.zhihu.com/p/377423211" class="bare">https://zhuanlan.zhihu.com/p/377423211</a></p>
</div>
<div class="ulist">
<ul>
<li>
<p>多线程情况下，访问共享资源会出现线程安全问题，Synchronized就是用来保证线程安全的</p>
</li>
<li>
<p>JMM java memory management, 主内存以及线程本地内存（Java内存模型的抽象概念）</p>
</li>
<li>
<p>使用Synchronized 时，会将本地内存中的共享变量删除，需要从主内存中获取</p>
</li>
<li>
<p>Synchronized 可以保证原子性、可见性、有序性</p>
</li>
<li>
<p>可实现悲观锁、非公平锁、排他锁、可重入锁</p>
</li>
<li>
<p>修饰普通同步方法、修改静态同步方法、修改同步代码块</p>
</li>
<li>
<p>锁基于对象实现的，对象在堆内存中存储的</p>
</li>
<li>
<p>主要包含对象头、实例数据、对象填充</p>
</li>
<li>
<p>锁信息主要在对象头，对象头里有个 MarkWord，标记中四种锁状态，无锁、偏向锁、轻量级锁、重量级锁</p>
</li>
<li>
<p>不同状态的锁存储了不同的信息</p>
</li>
<li>
<p>重量级锁信息存储在 ObjectMonitor 中，其中 _owner（持有锁的线程）、_WaitSet（等待的线程）和 _EntryList（阻塞的线程）字段比较重要</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>当多个线程访问同步代码块，首先会进入的 EntryList 中，通过 CAS 方式尝试将 Monitor 中的 owner 字段设置为当前线程，同时 count+1，如果 owner 就是当前线程，则重入次数加 1,
当获取的锁的线程调用 wait 方法，则将 owner 设置为 null, count 减1，重入减1，当前线程加入 waitset 中，等待唤醒
当线程执行完同步代码块，释放锁，count 减 1， 重入减 1。</p>
</div>
<div class="paragraph">
<p>Java 虚拟机是通过进入和退出Monitor对象来实现代码块同步和方法同步的，代码块同步使用 monitorenter 和 moniterexit 指令实现的，而方法同步是通过 Access flags 后面的标识来确定改方法是否为同步方法。</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_什么是aqs">36. 什么是AQS</h2>
<div class="sectionbody">
<div class="paragraph">
<p>AbstractQueuedSynchronizer 抽象类，是 JUC 包下的一个基类，很多类都是基于 AQS 实现了部分功能</p>
</div>
<div class="ulist">
<ul>
<li>
<p>AQS 提供一个有 volatile 修饰并采用 CAS 方式修改的 int 类型的 state 变量</p>
</li>
<li>
<p>AQS 中维护了一个双向链表，有 head，有 tail，每个节点都是 node 对象，将需要获取锁的对象存放在双向链表中</p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_aqs_唤醒节点_为何从后往前找">37. AQS 唤醒节点，为何从后往前找</h2>
<div class="sectionbody">
<div class="paragraph">
<p>和插入有关，当节点插入时，上一个指针指向上一个节点，当上一个节点的下一个指针还未指向当前节点，存在错过风险。
取消一个节点也类似，也先操作 prev 节点。</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_synchronized_和_reentrantlock_的区别">38. synchronized 和 ReentrantLock 的区别</h2>
<div class="sectionbody">
<div class="paragraph">
<p>从使用层面和原理层面进行说明
- synchronized 是 Java 关键字，是在 JVM 层面实现的互斥锁的方式，不需要手动加解锁，由 JVM 实现；ReentrantLock 是类，加解锁需要调用 lock unlock 方法。
- synchronized 只支持非公平锁，ReentrantLock 支持公平锁和非公平锁。
- synchronized 是基于 ObjectMonitor 实现的；ReentranLock 是基于 AQS 实现的。
- synchronized 发生异常会自动释放锁，不会造成死锁问题；ReentrantLock 需要手动释放，否则会导致死锁。
- synchronized 适用于少了同步代码块场景；ReentrantLock 适用于大量同步代码块场景。
- synchronized 不能判断锁状态；ReentrantLock 可以。
- synchronized 不可中断，需要线程执行完；ReentrantLock 可以中断。</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_reentrantreadwritelock_实现原理">39. ReentrantReadWriteLock 实现原理</h2>
<div class="sectionbody">
<div class="paragraph">
<p>还是基于 AQS 实现的，还是对 state 进行操作，拿到锁就执行，如果没拿到，就去 AQS 队列中排队</p>
</div>
<div class="paragraph">
<p>读操作，基于 state 的高 16 位进行操作
写操作，基于 state 的低 16 位进行操作</p>
</div>
<div class="paragraph">
<p>写重入：基本和 ReentrantLock 一致，持有锁资源的线程，是当前线程，state 低位加 1 即可。
读重入：读锁时共享锁，除了对高位 state 加 1，还需要在各个线程中，通过 ThreadLocal 记录重入次数</p>
</div>
<div class="paragraph">
<p>写锁饥饿问题：读锁时共享锁，如果大量读锁来读取资源，绕过写锁，那么会操作写锁无法获取到资源，所以读锁在获取锁之前，需要判断当前资源是否有写锁等待，如果有，则排在之后。</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_jdk_提供了几种线程池">40. JDK 提供了几种线程池</h2>
<div class="sectionbody">
<div class="paragraph">
<p>5种：
newFixedThreadPool 核心线程数和最大线程数一样，是固定值
newSingleThreadExecutor 核心线程数只有一个，任务队列是有序的，时候顺序执行的任务
newCachedThreadPool 没有核心线程数，最大线程数是Integer的最大值，当任务不断增加时，会自动创建线程执行 SynchronizedQueue，最大特点是，只有有任务，就会有线程执行
newScheduleThreadPool 可以以一定周期去执行一个任务，或者延迟多久执行一个任务一次，原理是用的DelayQueue实现延迟执行，周期任务是执行完成后，再次扔回阻塞队列。
newWorkStealingPool 基于ForkJoin实现，每个线程都有自己的阻塞队列，可以任务拆分，然后合并。</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_线程池的核心参数有什么">41. 线程池的核心参数有什么？</h2>
<div class="sectionbody">
<div class="paragraph">
<p>核心线程数、最大线程数、非核心线程数在无任务后存活时间、时间单位、阻塞队列，ThreadFactory构建线程的一些信息，拒绝策略</p>
</div>
<div class="paragraph">
<p>拒绝策略：
1. 抛出异常 AbortPolicy
2. 自行执行 CallerRunsPolicy
3. 丢弃 DiscardPolicy
4. DiscardOldestPolicy
5. 自定义策略，实现 RejectedExecutionHandler</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_线程池的状态">42. 线程池的状态</h2>
<div class="sectionbody">
<div class="paragraph">
<p>有一个 int 变量，前 3 位用于记录线程池的状态，有五种，分别是 RUNNING（构建后状态）、SHUTDOWN(调用shutdown方法，不会接收新任务，当所有任务执行完后，进入下一个状态)、
STOP（调用shutdownNow方法，不会接收新任务，队列中的任务也不会执行，每个现在也会执行中断方法）、tindying（线程关闭的过渡状态）、teiminated</p>
</div>
<div class="paragraph">
<p>20 线程池的执行流程</p>
</div>
<div class="paragraph">
<p>任务加入线程池，先判断任务是否是空，为空抛出异常；接着判断核心线程是否可以添加，没有则去创建（创建的时候也存在失败）
成功了，则继续执行，失败了以及核心线程创建完了，就判断线程池状态是否正常，处于Running，就加入阻塞队列中，在加入队列中的时候也可能
发生失败，如果成功了，则在判断线程池状态是否处于 Running 中，如果没有，则执行拒绝策略，然后结束，如果正常，判断工作线程是否是 0 个，不是 0 个，则直接执行，如果是 0 个
则创建非核心线程处理（可以设置核心线程数是0）。在加入队列失败后，判断是否能添加非核心线程数，添加失败，走拒绝策略，然后结束，添加成功则执行。</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_线程池添加工作线程的流程">43. 线程池添加工作线程的流程</h2>
<div class="sectionbody">
<div class="paragraph">
<p>addWorker
1. 校验线程池状态以及工作线程个数
2. 创建工作线程，然后启动线程</p>
</div>
<div class="paragraph">
<p>检查线程池状态，再判断工作线程个数，包括核心以及非核心线程，通过 CAS 进行 ctl 低 29 位进行 +1，接着创建 Woker 对象，里面包含一个 Thread，接着将 work 加入线程集合中也即一个 HashSet，
然后调用 start方法执行任务。</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_线程池为何要构建空任务的非核心线程">44. 线程池为何要构建空任务的非核心线程？</h2>
<div class="sectionbody">
<div class="paragraph">
<p>当阻塞队列中有任务，但是没有工作线程为 0，会导致任务饥饿，导致的原因有 2 个，创建核心线程的时候，可以设置成 0，第二个是可以设置属性运行核心线程数空闲多久后销毁。</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_线程池使用完后为何必须_shutdown">45. 线程池使用完后为何必须 shutdown ？</h2>
<div class="sectionbody">
<div class="paragraph">
<p>创建核心线程，创建一个 worker，内部是一个 Thread，属于 JVM 层面，是一个 GCRoot 节点，如果这个线程无法回收，那么 Worker 也无法回收，整个线程池也无法回收。
调用 shutdown，会改变线程池状态，没阻塞的现在在获取任务之前，其次也会终止所有空闲的线程。</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_线程池的核心参数如何设置">46. 线程池的核心参数如何设置</h2>
<div class="sectionbody">
<div class="paragraph">
<p>核心线程数、阻塞队列、拒绝策略设置</p>
</div>
<div class="paragraph">
<p>实际任务类型不确定，可能是cpu密集型，也可能是io密集型，还有混合型的，
还有各个部署的服务器不一样，执行时间也不一样，最好的方式是通过测试，将项目部署到测试环境或者沙箱环境，经过各种压测获得一个符合的参数。
但是每次修改项目都需要重新部署，成本太高，可通过线程池提供的查询、设置接口实现一个动态监控以及修改线程池方案，
也可通过开源的一些框架去做监控和修改 hippo4j</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_concurrenthashmap_优化点">47. ConcurrentHashMap 优化点</h2>
<div class="sectionbody">
<div class="ulist">
<ul>
<li>
<p>存储结构 数组+链表+红黑树，链表转红黑树，链表长度大于 8，数组长度大于 64。</p>
</li>
<li>
<p>存储操作 CAS + Synchronized 进行加锁，数据在数组上不需要加锁，通过 CAS 获取，如果在链表上，对 node 进行 Synchronized，锁的粒度细很多。</p>
</li>
<li>
<p>扩容操作，协助扩容，加快操作</p>
</li>
<li>
<p>计数器 基于 LongAddr 数组，分段统计，汇总总数</p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_concurrenthashmap_散列算法">48. ConcurrentHashMap 散列算法</h2>
<div class="sectionbody">
<div class="ulist">
<ul>
<li>
<p>hashcode 的高低位都能参与运算</p>
</li>
<li>
<p>保证最终的值不是负数</p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_concurrenthashmap_初始化数组的流程">49. ConcurrentHashMap 初始化数组的流程</h2>
<div class="sectionbody">
<div class="paragraph">
<p>数组是懒加载的，只有在放值的时候，初始化数组，sizeCtl 控制数组初始化和扩容的变量，-1 正在初始化 0 表示没有初始化，大于0 当前数组扩容阈值，或者当前数组的初始化大小</p>
</div>
<div class="paragraph">
<p>while循环加DCL（双重判断，中间加了一把锁，锁是CAS）, 判断通过后，初始化数组</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_concurrenthashmap_扩容流程">50. ConcurrentHashMap 扩容流程</h2>
<div class="sectionbody">
<div class="paragraph">
<p>触发方式：链表转红黑树，数组长度小于64， putAll 数组不够时，达到数据阈值时。
计算扩容标识戳，计算每个线程迁移的长度，初始化新数组，线程领取迁移任务，
老数据迁移到新数组，迁移结束后，判断是否是最后一个线程迁移完成，
如果是最后一个，还要再检查一次有没有遗漏的数据。</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_concurrenthashmap_读取数据的流程">51. ConcurrentHashMap 读取数据的流程？</h2>
<div class="sectionbody">
<div class="paragraph">
<p>get 不会加锁，将 key 进行 hashcode，获取到数组的索引位置，如果数据在数组上，直接返回，
如果不在，则继续查询链表或者红黑树，如果是查询链表，通过 next 进行查询，
如果数据有迁移，则在新 table 中查询，如果在红黑树中查询，如果没有写操作，那么会以 Ologn 进行查找，
如果有写操作，那么会查询原来的双向列表。</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_concurrenthashmap_计数器的实现">52. ConcurrentHashMap 计数器的实现</h2>
<div class="sectionbody">
<div class="paragraph">
<p>记录元素个数，为避免并发过高，计数效率太低，仿照 LongAddr，有一个 baseCount, 以及一个 CounterCell 数组，在并发量的情况下，向 CounterCell 中追加个数，当线程通过
size 方法统计元素个数的时候，将 baseCount 以及 CounterCell 数组中的value进行累加</p>
</div>
</div>
</div>
<h1 id="_mysql" class="sect0">MySQL</h1>
<div class="sect1">
<h2 id="_什么是_buffer_pool">53. 什么是 Buffer Pool ？</h2>
<div class="sectionbody">
<div class="paragraph">
<p><a href="https://www.cnblogs.com/better-farther-world2099/articles/14768929.html" class="bare">https://www.cnblogs.com/better-farther-world2099/articles/14768929.html</a></p>
</div>
<div class="paragraph">
<p>MySQL 数据是存放在磁盘中的，如果每次操作都是对磁盘进行，那么肯定很影响性能。所以一定是先把数据从磁盘中取出来，放在内存中，再在内存中操作。
这一块区域就是 buffer pool。它由许多的数据缓存页和控制块组成。默认大小时 128M，每页大小是 16k，控制块为数据页的 5%。实际应用中，对数据的增删改
都是在 buffer pool 中操作，然后再由 IO 线程写入磁盘。同时为了避免数据库崩溃等异常情况，导致未成功写入磁盘，引入了 redo 日志，记录了之前做过的增删改操作，
重启后，读取文件内操作，重新执行一遍，即可恢复。当然，对于数据更新过程，还有一套严格的步骤，涉及到 undolog、binlog、提交事务、buffer pool 占数据刷回磁盘等。
通过这种方式，保证在内存中操作，然后顺序写回磁盘，提高性能。（内存速度高于磁盘，顺序写高于随机读写）</p>
</div>
<div class="paragraph">
<p>工作流程为：在查询的时候，先在 buffer pool 中查询对应的数据页，如果没有从磁盘读取，存在 buffer pool 中，下次查询直接从 buffer pool 中获取。</p>
</div>
<div class="paragraph">
<p>不同于查询缓存，查询缓存是以 key-value 的形式，存储相同查询 sql 的结果。buffer pool 是属于引擎层的。</p>
</div>
<div class="paragraph">
<p>buffer pool 是以页为单位进行存储的，每个缓存页都对应一个控制块，里面包含数据页所属的表空间、编号以及在 buffer pool 中的地址。</p>
</div>
<div class="paragraph">
<p>buffer pool 中页分三种， 空闲页、干净页、脏页，空闲页是未被使用的页，干净页是指内存的数据和磁盘数据一致，脏页是指内存中的数据已被修改，还没有刷回磁盘，当刷回磁盘后变为干净页。</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_innodb_如何管理_page_页">54. InnoDB 如何管理 Page 页?</h2>
<div class="sectionbody">
<div class="paragraph">
<p>基于链表 + LRU + 冷热分离来管理的。</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_mysql_为什么要改进_lru_算法">55. MySQL 为什么要改进 LRU 算法?</h2>
<div class="sectionbody">
<div class="paragraph">
<p>LRU 是指最近最少使用算法，原理就是将使用的元素放在队列头部，这样尾部即为最少使用的元素，当队列满了，淘汰最末尾的即可。
但是在 MySQL 使用中会出现 2 个比较常见的问题:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>第一个是预读失效，所谓预读就是在读取一部分数据后，其相邻的数据也有可能下次被访问，所以可以提前预读提高性能，但是并不一定会被读取，所以预读后，导致淘汰了页，但是淘汰的页却是需要被访问的。</p>
</li>
<li>
<p>第二个是缓存池污染，当需要读取大量的页时，会同时加入多个页，淘汰已经存储的页,同样是不合理的，这就是缓存池污染，会导致 MySQL 性能下降。</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>MySQL 因此改进了 LRU 算法，将其分为2个区域，热数据区和冷数据区，当加载页时，先放在冷数据区，1s 内被再次访问就移动至热数据区，时间可设置。
为何设置成1s，是因为 1s 内如果数据不在被访问，那么再次被访问的几率就不大了。时间可以调整。</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_使用索引一定可以提高效率吗">56. 使用索引一定可以提高效率吗？</h2>
<div class="sectionbody">
<div class="paragraph">
<p>视情况而定，有好处也有坏处。</p>
</div>
<div class="paragraph">
<p>优点：
- 提高检索效率，降低数据库IO成本
- 通过对索引列进行排序，降低数据排序的成本，降低cpu的消耗
缺点：
- 创建和维护索引需要耗费时间，随数据量增加而增加
- 需要占用物理空间
- 数据进行增加、修改、 删除，索引也需要维护
创建索引的原则：
- 在经常需要搜索的列创建索引
- 主键、外键
- 范围搜索
- 排序
不建或少建索引：
- 用不到的字段查询
- 表记录比较少
- 经常需要修改的字段
- 参与列计算的
- 区分度不高的字段</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_介绍一下_page_页的结构">57. 介绍一下 Page 页的结构</h2>
<div class="sectionbody">
<div class="ulist">
<ul>
<li>
<p>File Header: 文件头，描述页的信息</p>
</li>
<li>
<p>Page Header: 页头，页的状态信息</p>
</li>
<li>
<p>Infimum/Supermum: 最小最大记录</p>
</li>
<li>
<p>User Records: 数据行</p>
</li>
<li>
<p>Free Space: 空闲空间，页中未被使用的空间</p>
</li>
<li>
<p>Page Directory: 页目录，存储用户记录的相对位置</p>
</li>
<li>
<p>File Trailer: 文件尾，校验页是否完整</p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_说以下聚簇索引和非聚簇索引">58. 说以下聚簇索引和非聚簇索引</h2>
<div class="sectionbody">
<div class="paragraph">
<p>聚簇索引，索引和数据在一起，非聚簇索引，索引和数据分开存储，索引的叶子节点指向了数据对应的位置</p>
</div>
<div class="paragraph">
<p>主键采用聚簇索引，采用B+数结构，叶子节点就是行记录。</p>
</div>
<div class="paragraph">
<p>聚簇索引的优点
- 范围查找
- 查找目标数据，不需要再次io回表查询
- 覆盖索引</p>
</div>
<div class="paragraph">
<p>缺点
- 插入速度严重依赖插入顺序
- 更新主键的代价很高，因为会导致行移动
- 二级索引访问，需要回表查询</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_索引有那几种类型">59. 索引有那几种类型</h2>
<div class="sectionbody">
<div class="ulist">
<ul>
<li>
<p>普通索引 没有任何限制</p>
</li>
<li>
<p>唯一索引 字段唯一，允许控制</p>
</li>
<li>
<p>主键索引，只能有一个，不允许控制</p>
</li>
<li>
<p>复合索引 可以在多个列创建索引</p>
</li>
<li>
<p>全文索引大数据量检索，使用全文索引会比like快很多，字符串、文本字段。</p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_介绍一下最佳左前缀法则">60. 介绍一下最佳左前缀法则</h2>
<div class="sectionbody">
<div class="paragraph">
<p>根据联合索引最左边的字段进行排序，然后再按照第二个排序，依次&#8230;&#8203;&#8230;&#8203;&#8230;&#8203;</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_什么是索引下推">61. 什么是索引下推</h2>
<div class="sectionbody">
<div class="paragraph">
<p>没有索引下推时，会根据索引来查找记录，然后根据 where 进行数据过滤，会造成多次回表，
使用索引下推后，在取出索引的同时，判断是否可以根据where条件进行数据过滤，减少回表次数，提高性能。</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_什么是自适应哈希索引">62. 什么是自适应哈希索引</h2>
<div class="sectionbody">
<div class="paragraph">
<p>InnoDB 引擎根据查询统计发现某一查询满足hash索引的数据结构特点并且达到一定次数，就建立一个hash索引，底层是散列表，存在于缓存中，只适合搜索等值的查询，范围查询是不能使用的。
其目的减少b+树路径搜索，可以根据索引快速定位到叶子节点。</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_为什么_like_以_开头索引会失效">63. 为什么 like 以 % 开头索引会失效</h2>
<div class="sectionbody">
<div class="paragraph">
<p>索引的创建是有序的，如果首字母无法确认，也即%在左侧，这意味着所有的索引都要进行判断，如果在右侧，则可以根据首字母从左到右依次比较，可以过滤一部分数据，是可以用到索引的。
当然还有一种特殊情况，如果查询的字段中都在组合索引中，那么也会走索引，也是全部扫描，但是组合索引由于不会存储完整的数据行，那么检索效率还是高于全表扫描的。</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_自增还是uuid">64. 自增还是UUID</h2>
<div class="sectionbody">
<div class="olist arabic">
<ol class="arabic">
<li>
<p>自增长度小</p>
</li>
<li>
<p>占用空间小</p>
</li>
<li>
<p>容易排序</p>
</li>
<li>
<p>容易暴露业务量</p>
</li>
<li>
<p>并发高，竞争自增锁会增加数据库吞吐能力</p>
</li>
<li>
<p>数据迁移或分库分表场景，不再适用</p>
</li>
</ol>
</div>
<div class="paragraph">
<p>UUID
- 不会冲突，数据拆分合并，都能保证主键全局唯一性
- 可以在应用层生成，提高数据库吞吐能力
- 影响插入速度，与自增相比，最大缺陷就是随机io，新增的uuid，值不一定比以前的大，无法按顺序放在数据后，涉及到插入旧的数据之间，造成数据移动，影响性能。
- 会消耗更多的存储空间，操作比整型慢</p>
</div>
<div class="paragraph">
<p>推荐还是使用自增。</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_innodb_和_myisam_的区别">65. InnoDB 和 MyISAM 的区别</h2>
<div class="sectionbody">
<div class="ulist">
<ul>
<li>
<p>事务和外键</p>
</li>
<li>
<p>锁机制</p>
</li>
<li>
<p>索引结构</p>
</li>
<li>
<p>并发处理能力</p>
</li>
<li>
<p>存储文件</p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_b_树和_b_树的区别">66. B 树和 B+ 树的区别</h2>
<div class="sectionbody">
<div class="paragraph">
<p>B树
- 每个节点包含索引和数据，如果数据量过多，会导致树的高度增加，增加磁盘io</p>
</div>
<div class="paragraph">
<p>B+树
- 一个节点有多个元素
- 叶子节点都冗余了非叶子节点数，有数据冗余
- 叶子节点有单项指针</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_为什么采用_b_树而不是_b_树">67. 为什么采用 B+ 树而不是 B 树</h2>
<div class="sectionbody">
<div class="paragraph">
<p>B+ 树在提高IO性能的同时还能解决 B 树遍历效率低的问题。B+ 树只要遍历叶子节点即可实现整颗树的遍历。</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_innodb中的_b_树是怎么产生的">68. Innodb中的 B+ 树是怎么产生的？</h2>
<div class="sectionbody">
<div class="paragraph">
<p>MySQL 以页为单位进行读取存储。页包含双向指针、页目录以及用户数据区域。页目录分组缩小查询范围、提高查询效率，以空间换取时间。</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_一个b_树大概能存放多少条索引记录">69. 一个B+树大概能存放多少条索引记录</h2>
<div class="sectionbody">
<div class="paragraph">
<p><code>show global status like 'Innodb_page_size'</code>
- 16384 , 16kb</p>
</div>
<div class="paragraph">
<p>MySQL设计者将 B+ Tree 的节点大小设置为一个页，这样只需要一次 io 就能全载入，每页大小16k，所以节点也是 16k，B+Tree的根节点保存在内存中，子节点才是存储在磁盘中。</p>
</div>
<div class="paragraph">
<p>计算公式： 根节点指针数 * 单个叶子节点的记录行数</p>
</div>
<div class="ulist">
<ul>
<li>
<p>根节点指针数： 假设用的int，占用4个字节，指针6个字节，那么一个节点就可以存储 1638/(4+6) = 1638 个索引指针</p>
</li>
<li>
<p>计算每个叶子节点记录数，假设每行1k，那么一页就可以存储 16/1 行数据</p>
</li>
<li>
<p>一颗高度为2的树可以存放的记录数是 1638 * 16 = 26208, 同理，每增加一层高度，就乘以 1638即可。</p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_explain">70. explain</h2>
<div class="sectionbody">
<div class="ulist">
<ul>
<li>
<p>表的读取顺序</p>
</li>
<li>
<p>那些索引可以被使用</p>
</li>
<li>
<p>那些索引真正被使用</p>
</li>
<li>
<p>表的直接引用</p>
</li>
<li>
<p>每张表有多少行被优化器查询了</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>id：操作顺序
select_type: 查询类型
table：查询的表
partitions：分区
type: 表示连接类型，以什么样的方式来获取数据，简化后的从好到坏 system const eq_ref ref rang index all
possible_keys: 可能使用到的索引
key：使用的索引
key_len：
ref:
rows: 读取行数
filtered：过滤行数
extra： 额外信息</p>
</div>
<div class="paragraph">
<p>type:
- system 不进行磁盘id，查询系统表，仅返回一条数据
- const 查询主键索引，返回 1 条或 0 条数据，属于精确查找
- eq_ref 查找唯一性索引，返回数据最多一条，属于精确查找
- ref 查询非唯一性索引，返回多条数据，属于精确查找
- range 查找某个索引的部分索引，只检索给定范围的行
- index 查找所有索引树，比all快
- all 不使用任何索引，全表扫描</p>
</div>
<div class="paragraph">
<p>extra:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>using filesort: 无法利用索引进行排序</p>
</li>
<li>
<p>using index: 直接通过索引就能获取到数据，也即覆盖索引，不需要回表</p>
</li>
<li>
<p>using index condition： 使用了索引，但是部分条件无法使用索引，会先用索引匹配一遍，在去匹配无法使用索引的条件</p>
</li>
<li>
<p>using join buffer： 使用了连接缓存 join 连接查询时，join 字段非索引字段</p>
</li>
<li>
<p>using temporary： 使用了临时表，来进行存储结果，常用于排序和分组查询</p>
</li>
<li>
<p>using where： 全表扫描</p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_如何进行分页查询优化">71. 如何进行分页查询优化</h2>
<div class="sectionbody">
<div class="paragraph">
<p>一般性分页，使用 limit offset,rows, 第一个是偏移量，0 开始，rows 表示返回的行数</p>
</div>
<div class="ulist">
<ul>
<li>
<p>使用覆盖索引</p>
</li>
<li>
<p>利用子查询，先定位偏移位置id，然后 id 值向后查询 select * from t5 where id&gt;=(select id from t5 order by text limit 1000000, 1) limit 10;</p>
</li>
<li>
<p>延迟关联，可以先用join进行分页，然后回表查询数据</p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_如何做慢查询优化">72. 如何做慢查询优化</h2>
<div class="sectionbody">
<div class="paragraph">
<p>开启慢查询日志：
- slow_query_log 开启还是关闭 默认关闭
- slow-query-log-file 日志储存路径
- long-query-time 慢查询阈值，大于此时间就是慢查询</p>
</div>
<div class="paragraph">
<p>性能下降原因：
- 等待时间长，锁表导致
- 执行时间长，查询语句烂，索引失效，join太多，服务器参数配置</p>
</div>
<div class="paragraph">
<p>慢查询优化思路
- 优先选择高并发的sql
- 定位性能优化的性能瓶颈， io数据访问，看是否用到了索引，cpu，看数据预算是否花费了太多时间，网络带宽
- 明确优化目标，确定优化程度用户体验好
- 通过 explain 入手，明确 sql 的执行状态
- 永远用小的结果集驱动大的结果集，获取连接次数少，减少资源消耗
- 尽可能在索引中完成排序
- 只获取自己需要的列
- 只使用最有效的过滤条件，最短路径获取数据
- 避免复杂的 join 和子查询，建议不超过 3 张表，将复杂的 sql 拆分成小 sql，通过代码层进行封装
- 合理设计索引并利用</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_hash_索引的优缺点">73. Hash 索引的优缺点</h2>
<div class="sectionbody">
<div class="ulist">
<ul>
<li>
<p>没有 hash 冲突的情况，查询数据比较快，如果有，则按链表进行逐行比较</p>
</li>
<li>
<p>只存储hash值和行指针，不存储数据</p>
</li>
<li>
<p>只支持等值比较查询</p>
</li>
<li>
<p>无法用于排序，范围查找</p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_说一下_innodb_内存相关的参数优化">74. 说一下 InnoDB 内存相关的参数优化</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Buffer Pool， 增大值，可减少对磁盘的 io 操作，在有大量事务进行更新、删除、插入时，可有效提高性能。</p>
</div>
<div class="paragraph">
<p>增加依据： 根据命中率，低于90，则可以考虑增加缓冲池大小。缓存查询次数 / (缓存查询次数 + 磁盘查询次数) * 100</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_innodb_日志相关参数优化">75. InnoDB 日志相关参数优化</h2>
<div class="sectionbody">
<div class="ulist">
<ul>
<li>
<p>默认16M</p>
</li>
<li>
<p>有过有 text/blog 大字段，</p>
</li>
<li>
<p>innodb_lob_buffer_size 缓存区大小</p>
</li>
<li>
<p>innodb_log_file_size 日志文件大小</p>
</li>
<li>
<p>设置过小，会导致频发触发检查，刷新脏页到磁盘，增加 io 次数，影响性能</p>
</li>
<li>
<p>设置过大，如果宕机，恢复时间过长</p>
</li>
<li>
<p>官网建议最少要承载一个小时业务日志量</p>
</li>
<li>
<p>可以通过抓取一段时间内的 log sequence number，这是一个全局参数（MySQL设计）</p>
</li>
<li>
<p>pager 分页工具获取一分钟获取的 log sequence number，在判断一个小时即可</p>
</li>
<li>
<p>存在业务量大小负载，导致算出比较大、比较小，实际还需要集合业务经验判断。</p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_innodb_io线程相关的参数优化">76. InnoDB io线程相关的参数优化？</h2>
<div class="sectionbody">
<div class="ulist">
<ul>
<li>
<p>将磁盘 io 转为内存查询</p>
</li>
<li>
<p>query_cache_size&amp;have_query_cache 查询缓存</p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_什么是写失效">77. 什么是写失效？</h2>
<div class="sectionbody">
<div class="paragraph">
<p>数据库一页 16k，操作系统一页 4k，需要分 4 次写入，如果只写了一部分，那么就会造成数据丢失。</p>
</div>
<div class="ulist">
<ul>
<li>
<p>双写缓冲</p>
</li>
<li>
<p>先写 DoubleWriter Buffer 内存缓存</p>
</li>
<li>
<p>写入共享表空间 2m，连续的内存空间，如果下一步失败，从这里进行恢复</p>
</li>
<li>
<p>再写入数据文件中</p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_如何进行join优化">78. 如何进行join优化</h2>
<div class="sectionbody">
<div class="ulist">
<ul>
<li>
<p>永远用小结果的表驱动大结果，本质是减少外层循环数据量</p>
</li>
<li>
<p>为匹配的条件增加索引，减少内层表的循环匹配次数</p>
</li>
<li>
<p>增大 join buffer size 的大小，缓存数据越多，内层表扫描次数就越少</p>
</li>
<li>
<p>减少不必要的字段查询。</p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_索引那些情况会失效">79. 索引那些情况会失效</h2>
<div class="sectionbody">
<div class="ulist">
<ul>
<li>
<p>查询条件包含 or</p>
</li>
<li>
<p>隐式类型转换</p>
</li>
<li>
<p>左 like</p>
</li>
<li>
<p>联合索引，违反最左匹配原则</p>
</li>
<li>
<p>对索引进行函数运算</p>
</li>
<li>
<p>多索引字段使用 != &lt;&gt; not in</p>
</li>
<li>
<p>索引使用 is null, is not null, 可能会失效</p>
</li>
<li>
<p>mysql 估计使用全表扫描会比使用索引快，则不适用索引。</p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_什么是覆盖索引">80. 什么是覆盖索引</h2>
<div class="sectionbody">
<div class="paragraph">
<p>获取的数据都在索引上，不需要回表</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_mysql_事务的特性">81. MySQL 事务的特性</h2>
<div class="sectionbody">
<div class="ulist">
<ul>
<li>
<p>原子性 事务作为一个整体被执行，那要全部执行，那么全部不执行，实现回滚是 undo log。</p>
</li>
<li>
<p>一致性 事务应确保数据库状态从一个一致性状态转变为另一个一致性状态，一致性是数据库中的数据应满足完整性约束</p>
</li>
<li>
<p>隔离性 一个事务的执行，不能被其他事务影响，有是个隔离级别，从低到高一次 读未提交，读已提交，可重复读、串行化</p>
</li>
<li>
<p>持久性 一个事务一旦提交，在数据库中的改变是永久性的，后续操作或故障都不会受影响，丢失，主要靠 redo log</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>事务的持久化是为了应对系统崩溃造成的数据丢失，只有保证了事务的一致性，才能保证执行结果的正确性。</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_mysql_可重复读是怎么实现的">82. MySQL 可重复读是怎么实现的？</h2>
<div class="sectionbody">
<div class="paragraph">
<p>MVCC 多版本控制器，核心就是 Undo log 多版本链 加 Read view, 通过 undo log 来保存数据的历史版本，实现多版本管理。CC 是通过 read view
来实现管理的，通过read view 原则来觉得数据是否显示。针对不同的隔离级别，read view 的生成策略不同，也就实现了不同的隔离级别。</p>
</div>
<div class="paragraph">
<p>Undo log 多版本链，每条数据都有两个隐藏字段
- trx_id: 事务id，记录最近一次更新这条数据的事务id
- roll_pointer: 回滚指针，指向之前生成的 undo log</p>
</div>
<div class="paragraph">
<p>Read view: 将某个时刻的数据记录下来，查询时从 undo log 最新一条记录进行对比，如果不符合比较规则，则回滚到上一条记录进行对比，直到得到符合比较条件的查询结果</p>
</div>
<div class="ulist">
<ul>
<li>
<p>m_ids: 那些事务正在执行，没有提交</p>
</li>
<li>
<p>min_trx_id:</p>
</li>
<li>
<p>max_trx_id:</p>
</li>
<li>
<p>creator_trx_id:</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>当一个事务查询sql时，会生成一致性视图 read-view, 按照规则来查找合适的数据</p>
</div>
<div class="paragraph">
<p>规则
- trx_id &lt; min_id: 事务已提交，可读
- trx_id &gt; max_id: 将来事务生成的，不可读
- 中间情况，分，当前在未提交的事务中，那么不可读，否则可读。</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_repeatable_read_解决了幻读问题嘛">83. Repeatable Read 解决了幻读问题嘛？</h2>
<div class="sectionbody">
<div class="paragraph">
<p>普通的查询时快照读，是不会看到别的事务插入的数据的， 只有在当前读时才会出现，例如 select &#8230;&#8203; for update 语句。
当前读读取的是数据库最新的数据。</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_数据库锁的种类">84. 数据库锁的种类</h2>
<div class="sectionbody">
<div class="paragraph">
<p>按操作类型：分读锁和写锁
按操作粒度分：表级锁、行级锁、页面锁 开销、速度、粒度、死锁、冲突概率、并发读
按操作性能： 乐观锁（数据版本比较，提交时检查）、悲观锁（提前锁定，避免其他人操作）</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_请说下共享锁和排他锁">85. 请说下共享锁和排他锁</h2>
<div class="sectionbody">
<div class="paragraph">
<p>行级锁包含共享锁和排他锁，必须在使用 InnoDB 引擎以及开始事务隔离级别为可重复读。</p>
</div>
<div class="paragraph">
<p>共享锁，当事务对数据加上共享锁后，其他用户可以并发读取数据，但不能进行修改，直到释放
排他锁，当事务加排他锁后，其他事务不能加任何类型的锁，获取锁，既可以读，也可以写</p>
</div>
<div class="paragraph">
<p>一般情况，insert update delete 都是默认加排他锁，select 默认不加锁，但是可以显示的镜像加共享锁和排他锁，lock in share mode, for update</p>
</div>
<div class="paragraph">
<p>共享锁兼容共享锁，其他组合都不兼容。</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_innodb_的行锁时怎么实现的">86. InnoDB 的行锁时怎么实现的？</h2>
<div class="sectionbody">
<div class="paragraph">
<p>通过对索引数据页上的记录加锁的，主要算法有3种： record lock/ gap lock / next-key lock</p>
</div>
<div class="ulist">
<ul>
<li>
<p>readlock 锁定当个行的记录</p>
</li>
<li>
<p>gap lock 间隙锁，锁定索引记录的间隙，确保索引记录的间隙不变</p>
</li>
<li>
<p>next-key lock 记录锁和间隙锁的组合，同时锁住数据以及数据前后范围
注：只有在索引上检索数据才会使用行级锁，否则会使用表锁</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>在可重复读的隔离级别下，先采用 next-key lock，当操作含有唯一所有时，会进行降级，recordlock，仅锁住索引本身而非范围</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_并发事务会产生那些问题">87. 并发事务会产生那些问题？</h2>
<div class="sectionbody">
<div class="ulist">
<ul>
<li>
<p>更新丢失 多个事务同事更新一行记录</p>
</li>
<li>
<p>脏读 一个事务读取到另一个事务修改但是未提交的数据</p>
</li>
<li>
<p>不可重复读</p>
</li>
<li>
<p>幻读</p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_mvcc_内部细节">88. mvcc 内部细节</h2>
<div class="sectionbody">
<div class="paragraph">
<p>数据库为了高并发的数据访问，对数据进行了多版本处理，并通过事务的可见性来保证事务看到自己应该看到的数据版本，只在 Read Commited，Repeatable Read 下工作</p>
</div>
<div class="paragraph">
<p>行记录有三个隐藏字段，db_row_id/db_trx_id/db_roll_ptr
- 没有显示定义主键，也没有定义唯一索引，会自动添加一个 row_id 隐藏列作为主键
- 事务进行增删改时，会将这个事务 id 插入到 trx_id 中
- roll_ptr： 回滚指针，指向 undo log</p>
</div>
<div class="paragraph">
<p>undo log 多版本链 read view</p>
</div>
<div class="paragraph">
<p>读已提交，每次都会生成一个 readview，导致每次都读到不同的数据
可重复读，一个事务中，只有第一次生成，可以解决不可重复读问题。</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_说一下_mysql_死锁的原因和处理方法">89. 说一下 MySQL 死锁的原因和处理方法</h2>
<div class="sectionbody">
<div class="paragraph">
<p>表锁，一个事务锁住了a表，然后访问b表，此时b被另一个事务锁住，同时需要访问a表，互相等待释放，造成死锁，调整程序操作逻辑</p>
</div>
<div class="paragraph">
<p>行级锁
- 全表扫描，行级锁上升为表锁，多个事务执行后，很容易产生死锁和阻塞，解决办法就是 explain 分析，处理全表扫描 sql，建立索引优化
- 两个事务想拿到对方持有的锁，互相等待，产生死锁。对索引加锁的顺序不一致导致，如果可以，尽量以相同的属性来访问索引记录，在程序批量处理数据时，如果对数据进行排序，以固定的顺序来处理记录，可降低死锁可能</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_mysql_体系结构">90. MySQL 体系结构</h2>
<div class="sectionbody">
<div class="ulist">
<ul>
<li>
<p>客户端连接器</p>
</li>
<li>
<p>连接池、sql 接口、解析器、查询优化器、缓存</p>
</li>
<li>
<p>存储引擎层，可插拔</p>
</li>
<li>
<p>系统文件层、 文件和日志</p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_undo_log_redo_log_bin_log">91. undo log、redo log、bin log</h2>
<div class="sectionbody">
<div class="paragraph">
<p>undo log 用于回滚的日志，更新之前会记录在这个里面，回滚时通过这个来回滚，在事务开始前产生，事务提交后，不会立即删除，放入待删除列表，交给后台线程处理。
- 提供回滚操作，记录回滚时需要操作的sql记录
- 提供多版本控制 mvcc</p>
</div>
<div class="paragraph">
<p>redo log：用于数据库持久性，事务提交成功，那么数据库修改就永久保存下来。
如果每执行成功一个事务，就将数据写入磁盘，会有严重的性能问题。因此设计了 redo log，来记录事务对那些数据页做了修改，就能解决性能问题，文件更小而且是顺序IO。
先将记录写入 redo log buffer，后续将多个操作写入redo log file。当发生故障、崩溃后，重启服务，可以通过此文件进行恢复。</p>
</div>
<div class="paragraph">
<p>bin log 用于记录数据库执行的写入性操作，不包含查询，以二进制的形式保持在磁盘中。
- 主从复制
- 数据恢复</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_说一下_redo_log_和_undo_log_的持久化策略">92. 说一下 redo log 和 undo log 的持久化策略</h2>
<div class="sectionbody">
<div class="ulist">
<ul>
<li>
<p>redo log 是 Innode 引擎独有的，binlog 是服务 server层的，其他层都可以用</p>
</li>
<li>
<p>redo logs 是物理记录，记录修改内容，binlog 是逻辑记录，记录 sql</p>
</li>
<li>
<p>redo log 是循环写，bin log 是追加写，不会覆盖以前的日志</p>
</li>
<li>
<p>redo 是事务自动恢复使用，binlog 用于主从复制以及数据恢复</p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_mysql_在线修改大表影响">93. MySQL 在线修改大表影响</h2>
<div class="sectionbody">
<div class="ulist">
<ul>
<li>
<p>时间不可预估，一般时间长</p>
</li>
<li>
<p>表级锁，影响写入操作</p>
</li>
<li>
<p>中断操作，会还原，会锁表</p>
</li>
<li>
<p>修改大表结构会导致cpu，io等性能消耗，使服务器性能降低</p>
</li>
<li>
<p>会导致主动延迟，影响业务读取</p>
</li>
<li>
<p>对表进行加锁</p>
</li>
<li>
<p>复制原表物理结构</p>
</li>
<li>
<p>修改表物理结构</p>
</li>
<li>
<p>将原表数据导入中间表</p>
</li>
<li>
<p>锁定中间表，删除原表</p>
</li>
<li>
<p>改名中间表</p>
</li>
<li>
<p>释放锁</p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_count_列名_count_1_count_区别">94. count(列名) count（1） count(*) 区别</h2>
<div class="sectionbody">
<div class="ulist">
<ul>
<li>
<p>count(*) 和count（1）不会忽略null数据，count(列名) 会忽略 null 数据</p>
</li>
<li>
<p>执行效率， innodb 通过遍历最小的二级索引来处理 count（*）和count（1），性能是相同的，大于 count 列名</p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_什么时候进行分库分表">95. 什么时候进行分库分表？</h2>
<div class="sectionbody">
<div class="paragraph">
<p>将同一个数据库中的数据分散存放到多个数据库上，以达到分散单台设备负载效果。主要解决因数据量过大导致数据库性能降低。</p>
</div>
<div class="ulist">
<ul>
<li>
<p>单机容量达到瓶颈</p>
</li>
<li>
<p>连接达到上限</p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_说说_mysql_的主从复制">96. 说说 Mysql 的主从复制</h2>
<div class="sectionbody">
<div class="ulist">
<ul>
<li>
<p>实时灾备，用于故障切换</p>
</li>
<li>
<p>备份</p>
</li>
<li>
<p>读写分离</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>是通过将主服务器的 binlog 负责到从服务器，然后执行。从服务器会启动一个 io 线程读取日志，以及一个执行线程，执行 binlog</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_sql执行流程">97. sql执行流程</h2>
<div class="sectionbody">
<div class="paragraph">
<p>客户端发送sql请求 -》查询缓存 -》 解析器-》解析树-》预处理器-》新解析树-》查询优化器-》执行计划-》查询执行引擎-》返回结果</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_mysql_支持缓存查询嘛">98. Mysql 支持缓存查询嘛？</h2>
<div class="sectionbody">
<div class="paragraph">
<p>5.7 支持，8.0 之后废弃</p>
</div>
<div class="ulist">
<ul>
<li>
<p>管理不灵活</p>
</li>
<li>
<p>sql 必须完全一致才会 cache 命中</p>
</li>
<li>
<p>结果太大不会缓存</p>
</li>
<li>
<p>分库分表不起作用</p>
</li>
<li>
<p>有自定义函数、触发器，不起作用</p>
</li>
<li>
<p>表结果、数据变化是，关联 cache 全部失效</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>可在应用层进行缓存，例如 redis encached 等等。</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_acid_靠什么保证">99. ACID 靠什么保证？</h2>
<div class="sectionbody">
<div class="paragraph">
<p>A: 原子性由 undo log 保证，他记录了需要回滚的日志信息，事务回滚时撤销已经执行成功的sql
C：一致性由其他三大特性保证，程序代码要保证业务上的一致性
I: 隔离性由 MVCC 来保证
D：持久性由内存 + redo log 来保证，宕机可以从 redo log 恢复。</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_mysql_主从同步原理">100. MySQL 主从同步原理</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Master 一个线程，binlog dump thread, 当 binlog 日志有变动时，线程读取内容发送给从节点。
从节点 IO 线程接收 binlog 内容，写入 relay log 文件中，从节点SQL线程读取 relay log 来进行重放。
默认采用异步负责，主库发送日志，不关心从库是否处理。存在丢失问题、但是性能高。可以选择全同步复制，必须等待从库响应才结束。
还有一种折中方式半同步复制，一个节点确认没问题，即可视为成功，其他节点就不考虑了。</p>
</div>
</div>
</div>
<h1 id="_redis" class="sect0">Redis</h1>
<div class="sect1">
<h2 id="_redis_为什么这么快">101. redis 为什么这么快</h2>
<div class="sectionbody">
<div class="ulist">
<ul>
<li>
<p>纯内存操作</p>
</li>
<li>
<p>单线程，避免上下文切换</p>
</li>
<li>
<p>渐进式rehash、缓存时间戳</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>渐进式rehash
- 数据量变大，涉及到数组的扩容，涉及到元素移动，需要 rehash 所有的key，如果需要一次性操作，那么数据量过大，消耗就比较耗时
- 采用 2 张全局hash表，当需要扩容时，需要将 hash 表1 的数据进行 rehash，拷贝到第二张表中，把一次大量的拷贝的开销，分摊到多次请求中。</p>
</div>
<div class="paragraph">
<p>缓存时间戳：
经常会使用ttl，如果每次从系统获取，那么比较耗时的，redis有一个定时任务，每毫秒更新一次，获取时间都是从缓存中获取</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_redis_使用场景">102. redis 使用场景</h2>
<div class="sectionbody">
<div class="ulist">
<ul>
<li>
<p>缓存</p>
</li>
<li>
<p>计数器</p>
</li>
<li>
<p>分布式会话</p>
</li>
<li>
<p>排行榜</p>
</li>
<li>
<p>最新列表</p>
</li>
<li>
<p>分布式锁</p>
</li>
<li>
<p>消息队列</p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_redis_为什么不采用多线程">103. redis 为什么不采用多线程</h2>
<div class="sectionbody">
<div class="ulist">
<ul>
<li>
<p>cpu 不是瓶颈，受制于内存、网络。</p>
</li>
<li>
<p>可以通过 pipeline 批量命令，每秒百万级别。</p>
</li>
<li>
<p>单线程，内部维护比较低。</p>
</li>
<li>
<p>如果是多线程，线程切换、加解锁，性能消耗，会导致死锁。</p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_redis_6_0_之后为何引入多线程">104. redis 6.0 之后为何引入多线程</h2>
<div class="sectionbody">
<div class="ulist">
<ul>
<li>
<p>单线程就够了 数据加载到内存，响应时间 100 纳秒，比较小的数据包，可以到达 8w-19w qps</p>
</li>
<li>
<p>多线程分摊 io 读写负载，读取还是单线程，为了扛住更多的并发</p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_redis_有哪些高级功能">105. redis 有哪些高级功能</h2>
<div class="sectionbody">
<div class="ulist">
<ul>
<li>
<p>慢查询</p>
</li>
<li>
<p>pipline 批量处理，节约往返时间</p>
</li>
<li>
<p>watch 可以用于监听某个数据是否被修改</p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_为什么要用redis">106. 为什么要用redis</h2>
<div class="sectionbody">
<div class="ulist">
<ul>
<li>
<p>高性能 可以將数据库磁盘访问变为内存访问，效率更高</p>
</li>
<li>
<p>高并发</p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_redis_和_memcached_相对比较有什么优势">107. redis 和 memcached 相对比较有什么优势</h2>
<div class="sectionbody">
<div class="ulist">
<ul>
<li>
<p>redis 支持内存、非关系型数据库， memcached 支持内存，支持 key-value</p>
</li>
<li>
<p>redis 支持多种数据类型 String List Set Zset Hash， memcached 支持 文本型、二进制类型</p>
</li>
<li>
<p>redis 支持单个操作、批量操作，支持弱事务、每个类型都有不同的crud，memcached crud以及少量其他命令</p>
</li>
<li>
<p>redis 支持发布、订阅、高可用、单线程操作，memcached支持多线程。</p>
</li>
<li>
<p>redis 支持持久化 RDB AOF，memcached 不支持持久化。</p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_怎么理解_redis_中的事务">108. 怎么理解 Redis 中的事务</h2>
<div class="sectionbody">
<div class="paragraph">
<p>事务表示一组操作，那么全部执行、要么全部不执行。</p>
</div>
<div class="paragraph">
<p>redis 提供简单事务功能，命令需要放在 multi exec 中间，事务功能很弱，如果存在语法错误，整个事务都无法执行，
如果发生运行时错误，也即使用了错误的命令操作，那么顺序执行的时，已经成功的命令无法回滚。</p>
</div>
<div class="paragraph">
<p>原理是，在执行multi命令后，redis 会设置成一个特殊的状态，在这个状态下用户执行命令不会被真的执行，而是被缓存起来，直到用户执行 exec 命令为止，才会将缓存中的命令依次执行。</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_redis_的过期策略以及内存淘汰机制">109. redis 的过期策略以及内存淘汰机制</h2>
<div class="sectionbody">
<div class="paragraph">
<p>过期策略
- 定期删除： 采用简单的贪心算法，每秒钟进行10次扫描，不会遍历所有字典的所有key，redis 会将设置了过期时间的key放在一个独立的字典中，
    从过期字典中随机 20 个key，删除其中过期的key，如果过期的 key 比率超过1/4，则重复操作。
    如果持续扫描字典，直到字典过期 key 变得稀疏，会造成卡顿，所以在开发的过程中需要留意，如果有大批量的 key 过期，需要给过期时间设置一个随机范围，而不能在同一时间过期。
    如果有从库，从库不会执行过期扫描，直接接受主库 key 过期，删除指令，因为是异步执行的，存在延迟。
- 惰性删除
    访问 key 的时候检查，如果过期了就删除，定期删除会导致 key 没有被及时删除。</p>
</div>
<div class="paragraph">
<p>淘汰机制
- noeviction： 不执行写，其他正常
- volatile-lru
- volatile-ttl
- volatile-random
- allkeys-lru
- allkeys-random</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_什么是缓存穿透_如何避免">110. 什么是缓存穿透？如何避免？</h2>
<div class="sectionbody">
<div class="paragraph">
<p>不正常的用户、黑客，如果缓存查询不到，会直接请求数据库，故意造数据，不在缓存中，直接去访问数据库</p>
</div>
<div class="paragraph">
<p>解决方案：</p>
</div>
<div class="paragraph">
<p>Bitmaps：bit 数组，每一个位置只存储 0 和 1, 1 表示存在，0 表示不存在。</p>
</div>
<div class="paragraph">
<p>布隆过滤器，判断一个元素是否在集合中。二进制数组加一个hash算法。存在误判，不在集合就一定不在集合，</p>
</div>
<div class="paragraph">
<p>在查询缓存之前，先查询布隆过滤器，判断是否存在，不存在直接返回，存在继续业务操作。</p>
</div>
<div class="paragraph">
<p>误判问题：
- 存在hash冲突，hash值相同，且在数组上，实际不一定存在
- hash值计算不在数组上，那么一定不存在</p>
</div>
<div class="paragraph">
<p>优化方案：
- 增大数组：减少 hash 冲突
- 增加 hash 函数，hash 都存在则存在</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_什么是缓存雪崩_如何避免">111. 什么是缓存雪崩？如何避免？</h2>
<div class="sectionbody">
<div class="paragraph">
<p>缓存雪崩：大量的 key 不经过 redis，全部访问数据库</p>
</div>
<div class="ulist">
<ul>
<li>
<p>redis 宕机，请求全部落到数据库</p>
</li>
<li>
<p>redis 大量 key ttl 失效</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>解决：
- redis 故障：引入集群，保证 redis 高可用，如果并发高，可以限流 hystrix
- ttl 尽量分散，加一个随机值</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_使用redis如何设计分布式锁">112. 使用redis如何设计分布式锁</h2>
<div class="sectionbody">
<div class="paragraph">
<p>锁是为了保证同一时间点，只有一个线程可以处理，在单机情况，可以通过同步机制进行数据加锁，保证同一个时间点只有一个线程可以处理，
但是分布式不再适用，需要借助外部锁进行，所有的服务都可以访问、请求锁，redis 提供了一个互斥能力，setnx, 如果key不存在，则设置值，否则什么也不做。
可以通过这个命令，达到互斥，实现一个分布式锁。</p>
</div>
<div class="paragraph">
<p>但是这个会操作死锁问题，一个程序拿到这个锁，但是由于各种原因，没有释放这个锁，导致其他客户端永远无法获取到这个锁，造成死锁问题。
通过引入 ttl 过期时间，可以解决这个问题，但是存在拿到锁的客户端，操作还没完成，ttl 过期了，可以通过守护线程，定时运行，检查 key 是否过期，如果快过期了，
就重新设置过期时间，进行一个锁续期，业务逻辑可以正常运行。</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_怎么使用_redis_实现消息队列">113. 怎么使用 redis 实现消息队列？</h2>
<div class="sectionbody">
<div class="ulist">
<ul>
<li>
<p>基于 list 的 LPUSH BRPOP，左边放，右边拿，足够简单，延迟几乎为 0，但是要处理空闲连接问题，如果线程一直阻塞，线程闲置久了，服务器会主动断开连接，后面使用会抛出异常，客户端消费需要注意，还有消费确认 ack 麻烦
不能做广播模式，不能重复消费</p>
</li>
<li>
<p>基于 sorted-set 实现： 用来实现延迟队列，消费者无法阻塞来获取数据，只有通过轮训，不允许重复消费</p>
</li>
<li>
<p>pub/sub 订阅、发布模式：一个消息可以发布到多个消费者，消息即时发送，不用等待消费者等待获取，消息一旦发布、客户端不在线，消息丢失，不能寻回</p>
</li>
<li>
<p>基于 Stream 类型实现： 消息队列的雏形，可以考虑在生产环境上使用，借鉴了kafka的设计，提供了很多命令操作，本身是一个链表，可以设置长度，存在消息丢失</p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_什么是_bigkey_会有什么影响">114. 什么是 bigkey？ 会有什么影响？</h2>
<div class="sectionbody">
<div class="paragraph">
<p>指key的value所占内存空间比较大，一个String最大512M。</p>
</div>
<div class="ulist">
<ul>
<li>
<p>字符串：一般认为超过 10kb，就是bigkey</p>
</li>
<li>
<p>非字符串： 体现在元素过多</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>危害：
- 内存空间不均匀
- 超时阻塞，操作bigkey比较耗时
- 网络阻塞，每次获取bigkey产生的网络流量较大
- 并不是完全致命，几乎不访问，那么也不重要。不要是热点key。</p>
</div>
<div class="paragraph">
<p>发现bigkey
- redis-cli bigkey
- 能做拆分的就拆分
- 能不使用就不用</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_redis_如何解决_key_冲突">115. redis 如何解决 key 冲突</h2>
<div class="sectionbody">
<div class="ulist">
<ul>
<li>
<p>业务隔离，使用不同的库，或者不同的实例</p>
</li>
<li>
<p>key 的设计，保证 key 的唯一性，比如业务模块+系统名称+关键字</p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_在怎么提高缓存命中率">116. 在怎么提高缓存命中率</h2>
<div class="sectionbody">
<div class="ulist">
<ul>
<li>
<p>提前加载</p>
</li>
<li>
<p>增加缓存存储空间，提高缓存数据量，提高命中率</p>
</li>
<li>
<p>调整缓存存储类型，存储对象用 hash 结构，可以用其他字段进行搜索，而不只是根据 id</p>
</li>
<li>
<p>提升缓存更新频次</p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_redis_的持久化">117. redis 的持久化</h2>
<div class="sectionbody">
<div class="paragraph">
<p>RDB: redis database , 快照，存储数据丢失
AOF： 记录每次操作命令，性能没有RDB高</p>
</div>
<div class="paragraph">
<p>一般采用混合模式，推荐。</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_为什么_redis_需要把所有的数据放到内存中">118. 为什么 redis 需要把所有的数据放到内存中？</h2>
<div class="sectionbody">
<div class="ulist">
<ul>
<li>
<p>内存中读取数据比磁盘读取快</p>
</li>
<li>
<p>cpu 始终要操作内存，将数据放在内存中，没有上下文切换，能避免再去磁盘读取数据带来的性能消耗</p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_如何保证缓存与数据库双写数据一致性">119. 如何保证缓存与数据库双写数据一致性？</h2>
<div class="sectionbody">
<div class="paragraph">
<p>双写新增没有问题</p>
</div>
<div class="ulist">
<ul>
<li>
<p>先更新缓存、在更新数据库 不考虑 数据不一致</p>
</li>
<li>
<p>先更新数据库，再更新缓存 不考虑 数据不一致</p>
</li>
<li>
<p>先删除缓存，后更新数据库 多线程操作也会导致数据不一致 延迟双删、更新数据库、休眠、再删除，休眠时间评估项目耗时，确保再次删除休眠期间产生的脏数据缓存</p>
</li>
<li>
<p>先更新数据库，后删除缓存</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>一般情况，删除缓存更新缓存比数据库操作要快，一般先更新数据库，后删除缓存。这种情况导致的不一致只有可能是查询比删除慢，而这种情况很少，同时配合延时双删，可以有效避免缓存不一致情况。</p>
</div>
<div class="paragraph">
<p>评估项目耗时，加上几百毫秒</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_redis_集群方案应该怎么做">120. redis 集群方案应该怎么做</h2>
<div class="sectionbody">
<div class="paragraph">
<p>redis 官方提供了集群方案 redis-cluster，采用虚拟槽分区。范围为 0 - 16383， 采用虚拟一致性 hash 分区，可以把所有的数据映射到一个固定范围的集合中，
槽是集群类数据管理和迁移的基本单位，采用大范围槽的主要目的是为了方便数据拆分和扩展，每个节点会负责一定数量的槽。</p>
</div>
<div class="ulist">
<ul>
<li>
<p>16383 控制集群数量，节点数量在1000内，足够了，如果更大，节点之间会 ping/pong 命令，如果节点过多，网络开销比较大</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>限制
- 批量操作支持有限
- 事务支持有限
- 只能使用一个库 0
- 复制结构只支持 1 层，不支持树型</p>
</div>
<div class="ulist">
<ul>
<li>
<p>可以根据 redis 命令手动搭建，不推荐命令</p>
</li>
<li>
<p>5.0 之前使用 ruby语言编写的脚本</p>
</li>
<li>
<p>5.0之后放弃ruby脚本，合并到了 redis-cli 中</p>
</li>
<li>
<p>集群中至少应该有奇数个节点，所以至少3个节点，官方推荐3主3从</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>启动、配置，核心是分配槽到那些机器上，加入减少会涉及到数据迁移，槽的变化。还有故障恢复、选举等功能。</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_redis_集群方案中什么情况导致整个集群不可用">121. redis 集群方案中什么情况导致整个集群不可用？</h2>
<div class="sectionbody">
<div class="paragraph">
<p>为保证集群的完整新，在所有的槽中任何一个没有指定节点，那么整个集群都不可用。</p>
</div>
<div class="paragraph">
<p>主节点故障，没有替代方案，整个集群不可用。</p>
</div>
<div class="paragraph">
<p>可以设置参数 cluster-require-full-coverage 为 no，这样只能是故障节点不可用，其他还是可用的。
特殊情况，设置参数后，半数主机都宕机了，根据分布式的一个经验，集群也不可用。</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_说一说_redis_哈希槽的概念">122. 说一说 redis 哈希槽的概念</h2>
<div class="sectionbody">
<div class="paragraph">
<p>分布式数据库需要将整个数据集按照分区规则映射到多个节点，每个节点负责一部分数据。重点是分区规则，常见的有 hash 分区和顺序分区。</p>
</div>
<div class="paragraph">
<p>hash 分区离散度好，数据分布业务无关，无法顺序访问，顺序分区则刚好相反。</p>
</div>
<div class="ulist">
<ul>
<li>
<p>简单hash</p>
</li>
<li>
<p>一致性hash分区， 每个节点分配一个token，加入节点、删除节点，只会影响相关的节点</p>
</li>
<li>
<p>虚拟一致性hash分区</p>
</li>
<li>
<p>虚拟槽分区： 固定范围是 16383</p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_redis_集群会有写操作丢失嘛_为什么">123. redis 集群会有写操作丢失嘛？为什么？</h2>
<div class="sectionbody">
<div class="paragraph">
<p>会，redis 复制中，主节点负责写，然后通过异步写入到从节点，如果主节点突然宕机了，导致命令无法同步到从节点。</p>
</div>
<div class="paragraph">
<p>158 redis 常见的性能和解决方案有哪些？</p>
</div>
<div class="ulist">
<ul>
<li>
<p>持久化问题，设置主从，主节点不做持久化，从节点做持久化</p>
</li>
<li>
<p>数据比较重要，slave 开启 Aof，每秒同步一次</p>
</li>
<li>
<p>主从复制，在同一个局域网</p>
</li>
<li>
<p>尽量避免主库压力很大，增加从库</p>
</li>
<li>
<p>主从复制 不要采用网状结构，采用线性结构</p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_什么是热点数据_什么是冷数据">124. 什么是热点数据，什么是冷数据？</h2>
<div class="sectionbody">
<div class="paragraph">
<p>热数据：经常被访问的数据、不断变化的数据（点赞、收藏、排行等等）
冷数据：</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_什么情况会导致_redis_阻塞">125. 什么情况会导致 Redis 阻塞?</h2>
<div class="sectionbody">
<div class="ulist">
<ul>
<li>
<p>客户端阻塞 命令 keys * Hgetall smemebers，时间复杂度On</p>
</li>
<li>
<p>bigkey 删除，释放内存</p>
</li>
<li>
<p>清空库</p>
</li>
<li>
<p>日志同步 aof 同步写</p>
</li>
<li>
<p>从库加载RDB文件</p>
</li>
</ul>
</div>
</div>
</div>
<h1 id="_spring_相关" class="sect0">Spring 相关</h1>
<div class="sect1">
<h2 id="_谈谈你对spring的理解">126. 谈谈你对Spring的理解</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Spring 更像是一个生态，提供了很多组件，来帮助开发项目。</p>
</div>
<div class="paragraph">
<p>spring的发展历史，近20年时间，发布了6个大版本，每个版本都有不同的升级</p>
</div>
<div class="paragraph">
<p>spring的组成，轻量级的应用框架，核心是IOC以及AOP，提供不同功能的模块，例如 SpringCore SpringContext SpringMVC Spring ORM等等</p>
</div>
<div class="paragraph">
<p>spring的好处，轻量，控制反转、aop，springmvc</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_spring_中应用了那些设计模式">127. Spring 中应用了那些设计模式</h2>
<div class="sectionbody">
<div class="paragraph">
<p>单例模式：默认创建的bean对象
原型模式：创建 bean 的时候可以设置 scope 属性
模板模式：jdbcTemplate，定义好流程，将需要实现的方法交给各个子类实现
观察者模式：监听事件
工厂模式：BeanFactory
代理模式：aop 实现</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_autowired_和_resource_的区别">128. Autowired 和 Resource 的区别</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Autowired 是 spring 提供的，默认根据类型注入对象的，也可以按照名称进行注入，结合 @Qualififier 注解一起使用
Resource 是 jsr 提供的，有两个重要属性，分别是name和type，使用了name，则按byName自动注入，使用了type则按照byType自动注入，如果都没使用，默认按照byName自动注入，如果都设置了，需要
同时满足，否则抛出异常。</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_spring_常用的注解">129. spring 常用的注解</h2>
<div class="sectionbody">

</div>
</div>
<div class="sect1">
<h2 id="_循环依赖的理解">130. 循环依赖的理解</h2>
<div class="sectionbody">
<div class="paragraph">
<p>循环依赖有三种情况，自身依赖自身，2个相互依赖，多个间接依赖
解决办法，在依赖之前，将自身先暴露。</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_spring_如何解决循环依赖">131. Spring 如何解决循环依赖</h2>
<div class="sectionbody">
<div class="paragraph">
<p><a href="https://www.bilibili.com/video/BV1dP411J7tQ/?spm_id_from=333.337.search-card.all.click&amp;vd_source=99f01a6cb2374a0363cc5576da4ce18a" class="bare">https://www.bilibili.com/video/BV1dP411J7tQ/?spm_id_from=333.337.search-card.all.click&amp;vd_source=99f01a6cb2374a0363cc5576da4ce18a</a></p>
</div>
<div class="paragraph">
<p>单例：构造注入，检查是否存在循环依赖，直接抛出异常，设置注入，三层缓存，提前暴露
原型：构造注入，直接抛出异常，设置注入，不支持</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_spring_三层缓存">132. Spring 三层缓存</h2>
<div class="sectionbody">
<div class="paragraph">
<p>bean.class --&#8594; 反射构造方法---&#8594; 普通对象 ---&#8594; 依赖 ----&#8594; 初始化前 ---&#8594; 初始化 ---&#8594; 初始化后（aop）---&#8594; 放入单例池</p>
</div>
<div class="ulist">
<ul>
<li>
<p>将需要的创建的放入一个正在创建的集合中，用于判断循环依赖</p>
</li>
<li>
<p>实例化对象，放入三级缓存，放入的是一个lambda表达式</p>
</li>
<li>
<p>填充依赖 --&#8594; 单例池找 ----&#8594; 正在创建集合中 --&#8594; 发现是循环依赖 ---&#8594; earlySingletonObjects 2级缓存 ---&#8594; 没找到继续在 SingletonFactory 三级缓存 ---&#8594; 通过lambda表达式 -&#8594; 判断是否存在aop（aop返回代理对象，否则直接对象）---&#8594; 放入 2级缓存 --&#8594; 填充</p>
</li>
<li>
<p>其他步骤（包括aop），根据2级缓存判断是否提前进行 aop 了。</p>
</li>
<li>
<p>加入到单例池中</p>
</li>
<li>
<p>从正在创建中移除</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>一级缓存：正式对象，单例池 SingletonObjects
二级缓存：半成品对象 earlySingletoObjects
三级缓存：对象工厂，提供对象，打破循环，只能获取一次，创建成功后，放入2级缓存，后续有循环依赖的，可以直接从2级缓存中获取了</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_spring_bean_的生命周期">133. Spring Bean 的生命周期</h2>
<div class="sectionbody">
<div class="paragraph">
<p>通过反射创建 bean 对象
设置对象属性
检测 Aware 相关接口依赖
前置处理的方法
检测是否实现了 InitializingBean 接口并调用
afterPropertiesSet
init-method 自定义
后置处理方法
使用
是否实现DisposableBean</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_spring支持几种作用域">134. Spring支持几种作用域</h2>
<div class="sectionbody">
<div class="paragraph">
<p>1 prototype: 为每一个bean提供一个实例
2 singleton: 默认，每个容器内只有一个bean实例，单例的模式有 BeanFactory 自身来维护的
3 request: 为每个网络请求创建一个实例，请求完成后，bean 失效会被垃圾回收期回收
4 session： 与 request 范围类似，确保每个session中有一个 bean 对象，bean 失效后，会被垃圾回收器回收
5 global-session: 全局作用域，与 Portlet 应用相关，当应用部署在 Portlet 容器中，包含很多的 portlet，如果要共用全局变量，那么需要存储在 global-session 中，效果和session作用域效果相同。</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_spring_事务的隔离级别">135. spring 事务的隔离级别</h2>
<div class="sectionbody">
<div class="paragraph">
<p>DEFAULT: 采用数据库默认的隔离级别
READ_UNCOMITTED: 读未提交，会产生脏读
READ_COMMITED: 读已提交，可以避免脏读，但不能解决幻读和不可重复读
REPEATABLE_READ: 可重复读，可以避免脏读和不可重复读，但是存在幻读
SERIALIZABLE: 串行化，最高事务隔离级别，挨个执行事务，性能低</p>
</div>
<div class="paragraph">
<p>事务隔离级别是指一个事务对数据修改与另一个并行事务的隔离程度，当多个事务访问相同的数据，如果没有采用必要的隔离机制，可能会发生脏读、幻读、不可重复读。</p>
</div>
<div class="paragraph">
<p>脏读，是指一个事务读取到了另一个事务还未提交的数据，另一个事务可能会对数据进行回滚，导致第一个事务读取到错误数据
幻读，事务A 按照一定条件进行数据读取， 期间事务B 插入了相同搜索条件的新数据，事务A再次按照原先条件进行读取时，发现了事务B 新插入的数据 称为幻读
不可重复读：事务B读取了两次数据资源，在这两次读取的过程中事务A修改了数据，导致事务B在这两次读取出来的数据不一致。</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_事务的传播行为">136. 事务的传播行为</h2>
<div class="sectionbody">
<div class="paragraph">
<p>PROPAGATION_REQUIRED: 支持当前事务，如果没有事务，则新建一个事务
PROPAGATION_SUPPOTRS: 支持当前事务，如果没有事务，则以非事务的方式执行
PROPAGATION_MANDATORY: 支持当前事务，如果没有事务，则抛出异常
PROPAGATION_REQUIRED_NEW: 新建事务，假设当前存在事务，把当前事务挂起
PROPAGATION_NEVER: 以非事务的方式执行，如果存在事务，则抛出异常
PROPAGATION_REQUIRED_NESTED: 如果存在事务，则嵌套执行，如果没有事务，则新建一个事务</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_spring_事务的实现方式">137. spring 事务的实现方式</h2>
<div class="sectionbody">
<div class="paragraph">
<p>编程式事务：手动编写事务代码，灵活性更高，维护度低，代码负责度
声明式事务：通过注解等方式，将业务代码和事务管理分离，交由 spring aop 进行处理</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_spring_事务的本质以及失效">138. spring 事务的本质以及失效</h2>
<div class="sectionbody">
<div class="ulist">
<ul>
<li>
<p>自身调用</p>
</li>
<li>
<p>异常被吃</p>
</li>
<li>
<p>异常类型抛出不对</p>
</li>
<li>
<p>@Transactional 注解属性 propgation 设置错误，以非事务的方式执行</p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_beanfactory_applicationcontext_理解">139. BeanFactory ApplicationContext  理解</h2>
<div class="sectionbody">

</div>
</div>
<div class="sect1">
<h2 id="_beanfactorypostprocessor_理解">140. BeanFactoryPostProcessor 理解</h2>
<div class="sectionbody">

</div>
</div>
<div class="sect1">
<h2 id="_beanpostprocessor">141. BeanPostProcessor</h2>
<div class="sectionbody">

</div>
</div>
<div class="sect1">
<h2 id="_谈谈_springmvc_的理解">142. 谈谈 SpringMVC 的理解</h2>
<div class="sectionbody">
<div class="paragraph">
<p>前端控制器，主要用于 web 请求，SpringMVC 有单独的 IOC 容器，和Spring 的IOC容器，存在父子关系，
Spring 无法获取到 SpringMVC 容器中的Bean，但 SpringMVC 可以访问 Spring。
主要流程是，客户端发送一个请求，经由 DispatchServlet 前端控制器，找到 HandlerMapping 处理器映射器，执行相关的处理器执行链，
然后找到处理器适配器 HandlerAdapter, 由处理器适配器找到对应的 handler，也即 Controller，执行业务后，
返回对应的 model and view 对象，再经由 DispatchServlet 调用视图解析器进行处理，然后返回给客户端。</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_谈谈_spring_和_springmvc_的关系">143. 谈谈 Spring 和 SpringMVC 的关系</h2>
<div class="sectionbody">

</div>
</div>
<div class="sect1">
<h2 id="_谈谈_delegatingfilterproxy_的理解">144. 谈谈 DelegatingFilterProxy 的理解</h2>
<div class="sectionbody">
<div class="paragraph">
<p>提供给第三方，将自定义的过滤器放入容器中</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_谈谈_springboot_的自动装配原理">145. 谈谈 SpringBoot 的自动装配原理</h2>
<div class="sectionbody">
<div class="paragraph">
<p>run 初始化IOC容器，SpringBootApplication 加载配置。自动装配主要是通过注解 SpringBootApplication 中的 EnableAutoConfiguration
以及 Import 导入META-INF 中的 spring.factory 定义好的配置类，以及 spring-autoconfigure-metadata.properties 需要过滤类, spi 去重、排除、过滤，然后配置</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_谈谈你对_import_注解的理解">146. 谈谈你对 Import 注解的理解</h2>
<div class="sectionbody">
<div class="ulist">
<ul>
<li>
<p>spring3.0提供，目的是替换 xml 配置中的 import 标签。</p>
</li>
<li>
<p>import 除了可以导入第三方的java配置类，还扩展了其他功能。</p>
</li>
<li>
<p>可以直接将某个类型对象注入到容器中。</p>
</li>
<li>
<p>如果导入的类实现了 ImportSelector，会调用声明的方法，然后将方法返回的全路径类注入到容器中。</p>
</li>
<li>
<p>如果导入的类实现了 ImportBeanDefinitionRegistrar 这个接口，会调用声明的方法，显示的提供注册器来完成注入。</p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_谈谈你对_deferredimportselector_的理解">147. 谈谈你对 DeferredImportSelector 的理解</h2>
<div class="sectionbody">
<div class="paragraph">
<p>在SpringBoot自动装配中核心是会加载META-INF/spring.factories文件中的配置信息。我们可以加载多个，那么会需要多次操作，我们可以考虑统一加载完，然后注入到容器中。</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_谈谈_bottstrap_yml_的理解">148. 谈谈 bottstrap.yml 的理解</h2>
<div class="sectionbody">
<div class="paragraph">
<p>只能适用于 SpringCloud 环境，作用是在容器启动之前，读取配置中心的配置信息。</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_如果要对属性文件中的账号密码加密如何实现">149. 如果要对属性文件中的账号密码加密如何实现？</h2>
<div class="sectionbody">
<div class="paragraph">
<p>在属性文件加载后，容器刷新之前，通过配置文件后置处理器，来进行密码解密， 然后替换成明文。</p>
</div>
<div class="ulist">
<ul>
<li>
<p>通过事件监听器，容器在加载完配置文件后，会发出一个事件，可以通过这个来处理</p>
</li>
<li>
<p>声明后置处理，定义一个类，实现 EnvironmentPostProcessor，实现里面解密、替换。</p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_谈谈_indexed_注解的作用">150. 谈谈 Indexed 注解的作用</h2>
<div class="sectionbody">
<div class="paragraph">
<p>spring5.0提供的，随着项目越来越复杂，通过 @ComponentScan 扫描加载的 class 会越来越多，启动会造成性能损耗，
@Component 用@Indexed标识，编译的时候，回收这些类，然后记录在 META-INF/spring.components 文件中，
系统启动时，只需要读取一个文件就可以了，提升效率，需要单独引入一个依赖。</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="__component_controller_repository_service_的区别">151. @Component @Controller @Repository @Service 的区别</h2>
<div class="sectionbody">
<div class="paragraph">
<p>都是 bean 对象，主要区分业务作用。</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_aop_有哪些通知类型">152. aop 有哪些通知类型</h2>
<div class="sectionbody">
<div class="paragraph">
<p>前置通知 @Before, 在执行 joinpoint 之前
后置通知 @AfterReturning 正常执行之后执行 @AfterReturning
异常通知 @AfterThrowing，执行抛出异常时执行
最终通知 @After 在执行完连接点方法后，无论是正常退出，还是异常退出，都会执行
环绕通知 @Around 在执行的前后执行。</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_介绍下_spring_中的依赖注入">153. 介绍下 Spring 中的依赖注入</h2>
<div class="sectionbody">
<div class="paragraph">
<p>通过 Spring 的依赖注入，不需要我们手动创建对象然后注入，而是 Spring 通过反射注入，我们只需要描述对象是如何创建的，由容器负责组装起来。</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_spring_中的单例_bean_是线程安全的吗">154. Spring 中的单例 Bean 是线程安全的吗？</h2>
<div class="sectionbody">
<div class="paragraph">
<p>spring本身没有提供bean的线程安全策略，对于原型，每次都是新对象，不存在线程安全问题，
对于单例bean，如果不存在共享属性，那么也是线程安全的，也可以采用 ThreadLocal 来处理线程安全的数据。</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="__componentscan_注解是干什么的">155. @ComponentScan 注解是干什么的？</h2>
<div class="sectionbody">
<div class="paragraph">
<p>指定扫描路径中的component类，springboot 默认是从 main 所在的类的目录，如果要扫描其他路径，就需要指定</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="__enableautoconfiguration_注解是干什么的">156. @EnableAutoConfiguration 注解是干什么的？</h2>
<div class="sectionbody">
<div class="paragraph">
<p>从命名上看，可以知晓是启动自动配置功能。
里面主要有 @Import 注解，导入了一个 AutoConfigurationImportSelector 类，这个类会读取 META-INF/spring.properties 配置，导入到内存。</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="__import_注解的三种用法">157. @Import 注解的三种用法</h2>
<div class="sectionbody">
<div class="ulist">
<ul>
<li>
<p>直接导入成bean对象</p>
</li>
<li>
<p>实现 ImportSelector，可以将返回的类完全限定名导入容器
-</p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_requestmapping_和_getmapping_的区别">158. RequestMapping 和 GetMapping 的区别</h2>
<div class="sectionbody">
<div class="ulist">
<ul>
<li>
<p>RequestMapping 是具有类属性的，可以指定 GET POST PUT 等请求方法，模糊的匹配</p>
</li>
<li>
<p>GetMapping 作用于方法，已经指明了，接受的请求必须是一个 Get 方法。</p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_springboot_的核心注解是那个_主要由那几个注解组成">159. SpringBoot 的核心注解是那个？主要由那几个注解组成？</h2>
<div class="sectionbody">
<div class="paragraph">
<p>核心注解是 @SpringBootAppliction， 核心注解有 @ComponentScan、 @SpringBootConfiguration、@EnableAutoConfiguration</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_springboot_可以兼容老项目吗">160. SpringBoot 可以兼容老项目吗？</h2>
<div class="sectionbody">
<div class="paragraph">
<p>可以，通过 @ImportResource 注解导入老的 Spring 配置文件。</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_springboot_如何定义多套不同环境的配置">161. SpringBoot 如何定义多套不同环境的配置</h2>
<div class="sectionbody">
<div class="paragraph">
<p>application-dev.yml
application-test.yml
application-prod.yml</p>
</div>
<div class="paragraph">
<p>在applicaiton.yml中配置当前环境 spring.profiles.active-test,多个用逗号隔开。</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_springboot_需要独立的容器吗">162. SpringBoot 需要独立的容器吗？</h2>
<div class="sectionbody">
<div class="paragraph">
<p>不需要，SpringBoot 有内置的 tomcat/jetty 容器</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_springboot_有哪几种读取配置的方式">163. SpringBoot 有哪几种读取配置的方式？</h2>
<div class="sectionbody">
<div class="paragraph">
<p>@PropertySource 读取配置文件，除了 properties文件，xml文件也可以，还可以配合 PropertySouceFactory，结合YAML解析器读取yml文件</p>
</div>
<div class="paragraph">
<p>@Value 读取单个属性，适用于参数比较少的情况， 通过${}方式</p>
</div>
<div class="paragraph">
<p>通过@Autowired 注入 Environment  类对象，通过getProperty获取属性</p>
</div>
<div class="paragraph">
<p>@ConfigurationProperties 使用于比较多的情况，可以指定前缀</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_springboot_支持那些日志框架">164. SpringBoot 支持那些日志框架</h2>
<div class="sectionbody">
<div class="paragraph">
<p>logging/log4j,lockback，默认使用lockback,如果需要使用其他的，需要移除lockback</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_springboot_springmvc_spring_有什么区别">165. SpringBoot SpringMVC Spring 有什么区别？</h2>
<div class="sectionbody">

</div>
</div>
<div class="sect1">
<h2 id="_springboot_的监视器是什么">166. SpringBoot 的监视器是什么？</h2>
<div class="sectionbody">
<div class="paragraph">
<p>访问到应用程序的当前状态，一些外部应用可以使用这些服务来触发报警信息，还提供了 http rest 端点来检查状态</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_springboot_打包成_jar_和普通的_jar_有什么区别">167. SpringBoot 打包成 jar 和普通的 jar 有什么区别？</h2>
<div class="sectionbody">
<div class="paragraph">
<p>SpringBoot 最终包，是可以执行的文件，通过 java -jar 命令，不能作为其他项目依赖即使依赖了也无法使用其中的类。
他们的结构不同，普通jar包解压就是包名，里面就是我们代码，而springboot在BOOT-INF里面，无法被直接引用。如果需要，在打包时增加配置，打包2个，一个可执行，一个可引用。</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_springboot_的_run_方法做了什么">168. SpringBoot 的 run 方法做了什么？</h2>
<div class="sectionbody">
<div class="paragraph">
<p>加载配置、创建应用上下文，ioc初始化操作</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_springboot_的优点">169. SpringBoot 的优点</h2>
<div class="sectionbody">
<div class="ulist">
<ul>
<li>
<p>可以独立运行，内嵌容器</p>
</li>
<li>
<p>简化配置 通过 spring-boot-starter 的方式，简化了 mave n配置</p>
</li>
<li>
<p>自动装配 组件的装配交由 starter 进行处理，约定大于配置</p>
</li>
<li>
<p>提供一些端点，提供健康检查等</p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_springboot_如何解决跨域问题">170. SpringBoot 如何解决跨域问题</h2>
<div class="sectionbody">
<div class="paragraph">
<p>通过实现 WebMvcConfigurer 接口，重写 addCorsMappings 方法解决跨域问题</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_springboot_如何配置_log4j">171. SpringBoot 如何配置 log4j</h2>
<div class="sectionbody">
<div class="ulist">
<ul>
<li>
<p>移除 lockback</p>
</li>
<li>
<p>增加 log4j 依赖</p>
</li>
<li>
<p>增加 log4j-spring.properties 配置文件</p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_springboot_如何实现定时任务">172. SpringBoot 如何实现定时任务</h2>
<div class="sectionbody">
<div class="ulist">
<ul>
<li>
<p>@Schedule 单体项目</p>
</li>
<li>
<p>第三方框架 @Quartz，使用分布式情况</p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_springboot_自动装配的核心配置文件有哪些">173. SpringBoot 自动装配的核心配置文件有哪些？</h2>
<div class="sectionbody">

</div>
</div>
<div class="sect1">
<h2 id="_springboot_自动装配的流程是怎么样的">174. SpringBoot 自动装配的流程是怎么样的？</h2>
<div class="sectionbody">

</div>
</div>
<div class="sect1">
<h2 id="_介绍几个常用的_starter">175. 介绍几个常用的 starter</h2>
<div class="sectionbody">

</div>
</div>
<div class="sect1">
<h2 id="_你如何理解_springboot_配置加载顺序">176. 你如何理解 SpringBoot 配置加载顺序</h2>
<div class="sectionbody">
<div class="ulist">
<ul>
<li>
<p>命令行参数</p>
</li>
<li>
<p>JNDI 参数</p>
</li>
<li>
<p>Java 系统参数</p>
</li>
<li>
<p>操作系统环境变量</p>
</li>
<li>
<p>jar包外部的带 profile的</p>
</li>
<li>
<p>jar包内部带 profile的</p>
</li>
<li>
<p>jar 外部不带profile的</p>
</li>
<li>
<p>jar 内部不带 profile的</p>
</li>
<li>
<p>@PropertySource指定的配置文件。</p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_如何实现_springboot_应用程序的安全性">177. 如何实现 SpringBoot 应用程序的安全性</h2>
<div class="sectionbody">
<div class="paragraph">
<p>引入 Security 安全框架</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_如何在_springboot_启动的时候运行一些特点的代码">178. 如何在 SpringBoot 启动的时候运行一些特点的代码</h2>
<div class="sectionbody">
<div class="paragraph">
<p>实现接口 ApplicationRunner 或者 CommandLineRunner，然后加入容器中即可。</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_如何重新加载_springboot_的更改_而无需重新启动服务器">179. 如何重新加载 SpringBoot 的更改，而无需重新启动服务器</h2>
<div class="sectionbody">

</div>
</div>
<div class="sect1">
<h2 id="_什么是_springboot_starter">180. 什么是 SpringBoot starter？</h2>
<div class="sectionbody">

</div>
</div>
<div class="sect1">
<h2 id="_什么是_springboot">181. 什么是 SpringBoot？</h2>
<div class="sectionbody">
<div class="paragraph">
<p>基于 Spring，基于约定大于配置，提供了自动装配来进行配置，旨在简化 Spring 应用的初始搭建和开发过程，从而使开发人员不需要定义样板化的配置。</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_我们如何连接一个像_mysql_的外部数据库">182. 我们如何连接一个像 MySQL 的外部数据库</h2>
<div class="sectionbody">
<div class="ulist">
<ul>
<li>
<p>引入数据库的连接包</p>
</li>
<li>
<p>配置对应的连接属性</p>
</li>
<li>
<p>启动即可</p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_运行_springboot_项目的方式">183. 运行 SpringBoot 项目的方式</h2>
<div class="sectionbody">
<div class="ulist">
<ul>
<li>
<p>打包成可执行jar文件，通过 java -jar 命令运行</p>
</li>
<li>
<p>打包成 war 文件，通过外部容器启动</p>
</li>
<li>
<p>通过 maven 或 gradle 插件进行运行</p>
</li>
</ul>
</div>
</div>
</div>
</div>
<div id="footer">
<div id="footer-text" style="text-align: center;">
<a href="https://beian.miit.gov.cn">蜀 ICP 备 2024094310 号</a> | <a href="http://www.beian.gov.cn/portal/registerSystemInfo?recordcode=51160302511654">川公网安备 51160302511654 号</a>  | 联系邮箱: zerothreeoneone@foxmail.com | 
更新于 2024-10-21 14:54:03 CST
</div>
</div>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/8.9.1/styles/github.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/8.9.1/highlight.min.js"></script>
<script>hljs.initHighlighting()</script>
</body>
</html>